Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -4320,6 +4320,13 @@ a = b[i] * c[i] + e; } +Note: the types ``float_t`` and ``double_t`` are defined in the math header file. +Their definition changes with the eval method. If they are used inside a scope +containing a ``#pragma clang fp eval_method`` their definition end up being +different than what the user might have intended. This can potentially generate +incorrect code, leading to an ABI mismatch. This case is prevented by emitting a +diagnostic. + The ``#pragma float_control`` pragma allows precise floating-point semantics and floating-point exception behavior to be specified for a section of the source code. This pragma can only appear at file or Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11499,6 +11499,10 @@ def err_objc_type_args_wrong_arity : Error< "too %select{many|few}0 type arguments for class %1 (have %2, expected %3)">; + +def err_type_definition_cannot_be_modified : Error< + "cannot use type '%0' within '#pragma clang fp eval_method'; type is set " + "according to the default eval method for the translation unit">; } def err_objc_type_arg_not_id_compatible : Error< Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -4601,6 +4601,23 @@ return false; } +StringRef EvalMethodValToStr(LangOptions::FPEvalMethodKind Kind) { + switch (Kind) { + case LangOptions::FPEvalMethodKind::FEM_Double: + return "double"; + case LangOptions::FPEvalMethodKind::FEM_Extended: + return "extended"; + case LangOptions::FPEvalMethodKind::FEM_Source: + return "source"; + default: + // The only way we land here is with the value of Kind being + // FEM_Inderterminable, and this should never happen. + assert((Kind != LangOptions::FPEvalMethodKind::FEM_Indeterminable) && + "unexpected eval method value"); + llvm_unreachable("unexpected eval method value"); + } +} + static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, QualType declSpecType, TypeSourceInfo *TInfo) { @@ -4971,6 +4988,18 @@ checkNullabilityConsistency(S, SimplePointerKind::Array, D.getDeclSpec().getTypeSpecTypeLoc()); } + if (T->isFloatingType() && + (T.getAsString() == "float_t" || T.getAsString() == "double_t")) + if (S.getLangOpts().getFPEvalMethod() != + LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine && + S.PP.getLastFPEvalPragmaLocation().isValid() && + S.PP.getCurrentFPEvalMethod() != S.getLangOpts().getFPEvalMethod()) + S.Diag(D.getIdentifierLoc(), + diag::err_type_definition_cannot_be_modified) + << T.getAsString() + << EvalMethodValToStr(S.PP.getCurrentFPEvalMethod()) + << EvalMethodValToStr(S.getLangOpts().getFPEvalMethod()); + } bool ExpectNoDerefChunk = Index: clang/test/Sema/Inputs/math.h =================================================================== --- /dev/null +++ clang/test/Sema/Inputs/math.h @@ -0,0 +1,43 @@ +#ifdef __FLT_EVAL_METHOD__ +#if __FLT_EVAL_METHOD__ == -1 +#define __GLIBC_FLT_EVAL_METHOD 2 +#else +#define __GLIBC_FLT_EVAL_METHOD __FLT_EVAL_METHOD__ +#endif +#elif defined __x86_64__ +#define __GLIBC_FLT_EVAL_METHOD 0 +#else +#define __GLIBC_FLT_EVAL_METHOD 2 +#endif + +# if __GLIBC_FLT_EVAL_METHOD == 0 || __GLIBC_FLT_EVAL_METHOD == 16 +typedef float float_t; +typedef double double_t; +# elif __GLIBC_FLT_EVAL_METHOD == 1 +typedef double float_t; +typedef double double_t; +# elif __GLIBC_FLT_EVAL_METHOD == 2 +typedef long double float_t; +typedef long double double_t; +# elif __GLIBC_FLT_EVAL_METHOD == 32 +typedef _Float32 float_t; +typedef double double_t; +# elif __GLIBC_FLT_EVAL_METHOD == 33 +typedef _Float32x float_t; +typedef _Float32x double_t; +# elif __GLIBC_FLT_EVAL_METHOD == 64 +typedef _Float64 float_t; +typedef _Float64 double_t; +# elif __GLIBC_FLT_EVAL_METHOD == 65 +typedef _Float64x float_t; +typedef _Float64x double_t; +# elif __GLIBC_FLT_EVAL_METHOD == 128 +typedef _Float128 float_t; +typedef _Float128 double_t; +# elif __GLIBC_FLT_EVAL_METHOD == 129 +typedef _Float128x float_t; +typedef _Float128x double_t; +# else +# error "Unknown __GLIBC_FLT_EVAL_METHOD" +# endif + Index: clang/test/Sema/abi-check-1.cpp =================================================================== --- /dev/null +++ clang/test/Sema/abi-check-1.cpp @@ -0,0 +1,121 @@ +// RUN: %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -emit-llvm -o - %s | FileCheck %s + +// RUN: %clang_cc1 -isystem %S/Inputs -ffp-eval-method=source \ +// RUN: -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s + +// RUN: not %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -ffp-eval-method=double -emit-obj -o %t %s 2>&1 \ +// RUN: | FileCheck -check-prefix=ERROR %s + +// RUN: not %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -ffp-eval-method=extended -emit-obj -o %t %s 2>&1 \ +// RUN: | FileCheck -check-prefix=ERROR %s + +#include + +float foo1() { +#pragma clang fp eval_method(source) + float a; + double b; + // CHECK: alloca float + // CHECK: alloca double + return a - b; +} + +float foo2() { +#pragma clang fp eval_method(source) + float_t a; + double_t b; + // CHECK: alloca float + // CHECK: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return a - b; +} + +void foo3() { +#pragma clang fp eval_method(source) + char buff[sizeof(float_t)]; + char bufd[sizeof(double_t)]; + // CHECK: alloca [4 x i8] + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + buff[1] = bufd[2]; +} + +float foo4() { +#pragma clang fp eval_method(source) + typedef float_t FT; + typedef double_t DT; + FT a; + DT b; + // CHECK: alloca float + // CHECK: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return a - b; +} + +int foo5() { +#pragma clang fp eval_method(source) + int t = _Generic( 1.0L, float_t:1, default:0); + int v = _Generic( 1.0L, double_t:1, default:0); + // CHECK: alloca i32 + // CHECK: alloca i32 + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return t; +} + +void foo6() { +#pragma clang fp eval_method(source) + auto resf = [](float_t f) { return f; }; + auto resd = [](double_t g) { return g; }; + // CHECK: alloca %class.anon + // CHECK: alloca %class.anon + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit +} + +void foo7() { +#pragma clang fp eval_method(source) + float f = (float_t)1; + double d = (double_t)2; + // CHECK: alloca float + // CHECK: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit +} + +void foo8() { +#pragma clang fp eval_method(source) + using Ft = float_t; + using Dt = double_t; + // CHECK: alloca float + // CHECK: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + Ft a; + Dt b; +} + +void foo9() { +#pragma clang fp eval_method(source) + float c1 = (float_t)12; + double c2 = (double_t)13; + // CHECK: alloca float + // CHECK: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit +} + +float foo10() { +#pragma clang fp eval_method(source) + extern float_t f; + extern double_t g; + // CHECK: load float + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return f-g; +} Index: clang/test/Sema/abi-check-2.cpp =================================================================== --- /dev/null +++ clang/test/Sema/abi-check-2.cpp @@ -0,0 +1,131 @@ +// RUN: %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -emit-llvm -o - %s | FileCheck %s + +// RUN: not %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -ffp-eval-method=source -emit-obj -o %t %s 2>&1 \ +// RUN: | FileCheck -check-prefix=ERROR %s + +// RUN: %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -ffp-eval-method=double -emit-llvm -o - %s \ +// RUN: | FileCheck -check-prefix=CHECK-DBL %s + +// RUN: not %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -ffp-eval-method=extended -emit-obj -o %t %s 2>&1 \ +// RUN: | FileCheck -check-prefix=ERROR %s + +#include + +float foo1() { +#pragma clang fp eval_method(double) + float a; + double b; + // CHECK: alloca float + // CHECK: alloca double + return a - b; +} + +float foo2() { +#pragma clang fp eval_method(double) + float_t a; + double_t b; + // CHECK: alloca float + // CHECK: alloca double + // CHECK-DBL: alloca double + // CHECK-DBL: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return a - b; +} + +void foo3() { +#pragma clang fp eval_method(double) + char buff[sizeof(float_t)]; + char bufd[sizeof(double_t)]; + // CHECK: alloca [4 x i8] + // CHECK: alloca [8 x i8] + // CHECK-DBL: alloca [8 x i8] + // CHECK-DBL: alloca [8 x i8] + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + buff[1] = bufd[2]; +} + +float foo4() { +#pragma clang fp eval_method(double) + typedef float_t FT; + typedef double_t DT; + FT a; + DT b; + // CHECK: alloca float + // CHECK: alloca double + // CHECK-DBL: alloca double + // CHECK-DBL: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return a - b; +} + +int foo5() { +#pragma clang fp eval_method(double) + int t = _Generic( 1.0L, float_t:1, default:0); + int v = _Generic( 1.0L, double_t:1, default:0); + // CHECK: alloca i32 + // CHECK: alloca i32 + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return t; +} + +void foo6() { +#pragma clang fp eval_method(double) + auto resf = [](float_t f) { return f; }; + auto resd = [](double_t g) { return g; }; + // CHECK: alloca %class.anon + // CHECK: alloca %class.anon + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit +} + +void foo7() { +#pragma clang fp eval_method(double) + float f = (float_t)1; + double d = (double_t)2; + // CHECK: alloca float + // CHECK: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit +} + +void foo8() { +#pragma clang fp eval_method(double) + using Ft = float_t; + using Dt = double_t; + // CHECK: alloca float + // CHECK: alloca double + // CHECK-DBL: alloca double + // CHECK-DBL: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + Ft a; + Dt b; +} + +void foo9() { +#pragma clang fp eval_method(double) + float c1 = (float_t)12; + double c2 = (double_t)13; + // CHECK: alloca float + // CHECK: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit +} + +float foo10() { +#pragma clang fp eval_method(double) + extern float_t f; + extern double_t g; + // CHECK: load double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return f-g; +} Index: clang/test/Sema/abi-check-3.cpp =================================================================== --- /dev/null +++ clang/test/Sema/abi-check-3.cpp @@ -0,0 +1,133 @@ +// RUN: %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -emit-llvm -o - %s | FileCheck %s + +// RUN: not %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -ffp-eval-method=source -emit-obj -o %t %s 2>&1 \ +// RUN: | FileCheck -check-prefix=ERROR %s + +// RUN: not %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -ffp-eval-method=double -emit-obj -o %t %s 2>&1 \ +// RUN: | FileCheck -check-prefix=ERROR %s + +// RUN: %clang_cc1 -isystem %S/Inputs -triple x86_64-linux-gnu \ +// RUN: -ffp-eval-method=extended -emit-llvm -o - %s \ +// RUN: | FileCheck -check-prefix=CHECK-EXT %s + +#include + +float foo1() { +#pragma clang fp eval_method(extended) + float a; + double b; + // CHECK: alloca float + // CHECK: alloca double + return a - b; +} + +float foo2() { +#pragma clang fp eval_method(extended) + float_t a; + double_t b; + // CHECK: alloca float + // CHECK: alloca double + // CHECK-EXT: alloca x86_fp80 + // CHECK-EXT: alloca x86_fp80 + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return a - b; +} + +void foo3() { +#pragma clang fp eval_method(extended) + char buff[sizeof(float_t)]; + char bufd[sizeof(double_t)]; + // CHECK: alloca [4 x i8] + // CHECK: alloca [8 x i8] + // CHECK-DBL: alloca [8 x i8] + // CHECK-DBL: alloca [8 x i8] + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + buff[1] = bufd[2]; +} + +float foo4() { +#pragma clang fp eval_method(extended) + typedef float_t FT; + typedef double_t DT; + FT a; + DT b; + // CHECK: alloca float + // CHECK: alloca double + // CHECK-EXT: alloca x86_fp80 + // CHECK-EXT: alloca x86_fp80 + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return a - b; +} + +int foo5() { +#pragma clang fp eval_method(extended) + int t = _Generic( 1.0L, float_t:1, default:0); + int v = _Generic( 1.0L, double_t:1, default:0); + // CHECK: alloca i32 + // CHECK: alloca i32 + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return t; +} + +void foo6() { +#pragma clang fp eval_method(extended) + auto resf = [](float_t f) { return f; }; + auto resd = [](double_t g) { return g; }; + // CHECK: alloca %class.anon + // CHECK: alloca %class.anon + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit +} + +void foo7() { +#pragma clang fp eval_method(extended) + float f = (float_t)1; + double d = (double_t)2; + // CHECK: alloca float + // CHECK: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit +} + +void foo8() { +#pragma clang fp eval_method(extended) + using Ft = float_t; + using Dt = double_t; + // CHECK: alloca float + // CHECK: alloca double + // CHECK-EXT: alloca x86_fp80 + // CHECK-EXT: alloca x86_fp80 + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + Ft a; + Dt b; +} + +void foo9() { +#pragma clang fp eval_method(extended) + float c1 = (float_t)12; + double c2 = (double_t)13; + // CHECK: alloca float + // CHECK: alloca double + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit +} + +float foo10() { +#pragma clang fp eval_method(extended) + extern float_t f; + extern double_t g; + // CHECK: load double + // CHECK-EXT: load x86_fp80 + // CHECK-EXT: load x86_fp80 + // ERROR: error: cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + // ERROR: error: cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit + return f-g; +}