diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1188,9 +1188,6 @@ // The C standard 7.6.1p2 says "The [FENV_ACCESS] pragma shall occur either // outside external declarations or preceding all explicit declarations and // statements inside a compound statement. -def warn_stdc_fenv_round_not_supported : - Warning<"pragma STDC FENV_ROUND is not supported">, - InGroup; def warn_stdc_unknown_rounding_mode : Warning< "invalid or unsupported rounding mode in '#pragma STDC FENV_ROUND' - ignored">, InGroup; diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -468,6 +468,43 @@ return true; } +namespace { +/// Cleanup action that restores floating-point control modes upon leaving +/// a scope. +struct FPControlModesCleanup final : EHScopeStack::Cleanup { + llvm::Value *PreviousModes; + FPControlModesCleanup(llvm::Value *M) : PreviousModes(M) {} + void Emit(CodeGenFunction &CGF, Flags flags) override { + // TODO: using 'setmode' must be more efficient. + CGF.Builder.CreateIntrinsic(llvm::Intrinsic::set_rounding, {}, + {PreviousModes}); + } +}; +} // namespace + +void CodeGenFunction::emitSetFPControlModes(const CompoundStmt &S) { + if (!S.hasStoredFPFeatures()) + return; + FPOptionsOverride FPO = S.getStoredFPFeatures(); + FPOptions FP = FPO.applyOverrides(CurFPFeatures); + llvm::RoundingMode OldRM = CurFPFeatures.getRoundingMode(); + llvm::RoundingMode NewRM = FP.getRoundingMode(); + if (OldRM == NewRM) + return; + llvm::Value *PreviousRM = nullptr; + if (OldRM == llvm::RoundingMode::Dynamic) { + llvm::Function *FGetRound = CGM.getIntrinsic(llvm::Intrinsic::flt_rounds); + PreviousRM = Builder.CreateCall(FGetRound); + } else { + PreviousRM = llvm::ConstantInt::get(Int32Ty, static_cast(OldRM)); + } + if (NewRM != llvm::RoundingMode::Dynamic) + Builder.CreateIntrinsic( + llvm::Intrinsic::set_rounding, {}, + llvm::ConstantInt::get(Int32Ty, static_cast(NewRM))); + EHStack.pushCleanup(NormalAndEHCleanup, PreviousRM); +} + /// EmitCompoundStmt - Emit a compound statement {..} node. If GetLast is true, /// this captures the expression result of the last sub-statement and returns it /// (for use by the statement expression extension). @@ -491,6 +528,9 @@ assert((!GetLast || (GetLast && ExprResult)) && "If GetLast is true then the CompoundStmt must have a StmtExprResult"); + // Optionally set up the new FP environment, if the compound statement + // contains a pragma that modifies it. + emitSetFPControlModes(S); CGFPOptionsRAII SavedFPFeatues(*this, S.getNewFPOptions(CurFPFeatures)); Address RetAlloca = Address::invalid(); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3013,6 +3013,10 @@ /// Get the record field index as represented in debug info. unsigned getDebugInfoFIndex(const RecordDecl *Rec, unsigned FieldIndex); + /// Optionally emit code that sets required floating-point control modes (like + /// rounding direction). Also creates corresponding cleanup action if the code + /// is emitted. + void emitSetFPControlModes(const CompoundStmt &S); //===--------------------------------------------------------------------===// // Declaration Emission diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -3279,9 +3279,6 @@ return; } - // Until the pragma is fully implemented, issue a warning. - PP.Diag(Tok.getLocation(), diag::warn_stdc_fenv_round_not_supported); - MutableArrayRef Toks(PP.getPreprocessorAllocator().Allocate(1), 1); Toks[0].startToken(); diff --git a/clang/test/CodeGen/complex-strictfp.c b/clang/test/CodeGen/complex-strictfp.c --- a/clang/test/CodeGen/complex-strictfp.c +++ b/clang/test/CodeGen/complex-strictfp.c @@ -15,16 +15,18 @@ // CHECK-LABEL: @test3a( // CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.set.rounding(i32 2) #[[ATTR2:[0-9]+]] // CHECK-NEXT: [[TMP0:%.*]] = load double, double* @D, align 8 // CHECK-NEXT: [[CF_REAL:%.*]] = load float, float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4 // CHECK-NEXT: [[CF_IMAG:%.*]] = load float, float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 1), align 4 -// CHECK-NEXT: [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR2:[0-9]+]] +// CHECK-NEXT: [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: [[CONV1:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_IMAG]], metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[CONV]], double [[TMP0]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: [[CONV2:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[ADD_R]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: [[CONV3:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[CONV1]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: store float [[CONV2]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4 // CHECK-NEXT: store float [[CONV3]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 1), align 4 +// CHECK-NEXT: call void @llvm.set.rounding(i32 1) #[[ATTR2]] // CHECK-NEXT: ret void // void test3a(void) { @@ -33,6 +35,7 @@ // CHECK-LABEL: @test3b( // CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.set.rounding(i32 2) #[[ATTR2]] // CHECK-NEXT: [[CF_REAL:%.*]] = load float, float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4 // CHECK-NEXT: [[CF_IMAG:%.*]] = load float, float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 1), align 4 // CHECK-NEXT: [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[CF_REAL]], metadata !"fpexcept.strict") #[[ATTR2]] @@ -40,6 +43,7 @@ // CHECK-NEXT: [[TMP0:%.*]] = load double, double* @D, align 8 // CHECK-NEXT: [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[TMP0]], double [[CONV]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: store double [[ADD_R]], double* @D, align 8 +// CHECK-NEXT: call void @llvm.set.rounding(i32 1) #[[ATTR2]] // CHECK-NEXT: ret void // void test3b(void) { @@ -48,6 +52,7 @@ // CHECK-LABEL: @test3c( // CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.set.rounding(i32 2) #[[ATTR2]] // CHECK-NEXT: [[G1_REAL:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 0), align 8 // CHECK-NEXT: [[G1_IMAG:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 1), align 8 // CHECK-NEXT: [[CF_REAL:%.*]] = load float, float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4 @@ -61,6 +66,7 @@ // CHECK-NEXT: [[CONV3:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[TMP1]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: store float [[CONV2]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4 // CHECK-NEXT: store float [[CONV3]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 1), align 4 +// CHECK-NEXT: call void @llvm.set.rounding(i32 1) #[[ATTR2]] // CHECK-NEXT: ret void // void test3c(void) { @@ -69,12 +75,14 @@ // CHECK-LABEL: @test3d( // CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.set.rounding(i32 2) #[[ATTR2]] // CHECK-NEXT: [[G1_REAL:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 0), align 8 // CHECK-NEXT: [[G1_IMAG:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 1), align 8 // CHECK-NEXT: [[TMP0:%.*]] = load double, double* @D, align 8 // CHECK-NEXT: [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[G1_REAL]], double [[TMP0]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: store double [[ADD_R]], double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 0), align 8 // CHECK-NEXT: store double [[G1_IMAG]], double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 1), align 8 +// CHECK-NEXT: call void @llvm.set.rounding(i32 1) #[[ATTR2]] // CHECK-NEXT: ret void // void test3d(void) { @@ -83,12 +91,14 @@ // CHECK-LABEL: @test3e( // CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.set.rounding(i32 2) #[[ATTR2]] // CHECK-NEXT: [[TMP0:%.*]] = load double, double* @D, align 8 // CHECK-NEXT: [[G1_REAL:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 0), align 8 // CHECK-NEXT: [[G1_IMAG:%.*]] = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 1), align 8 // CHECK-NEXT: [[ADD_R:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[TMP0]], double [[G1_REAL]], metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: store double [[ADD_R]], double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 0), align 8 // CHECK-NEXT: store double [[G1_IMAG]], double* getelementptr inbounds ({ double, double }, { double, double }* @g1, i32 0, i32 1), align 8 +// CHECK-NEXT: call void @llvm.set.rounding(i32 1) #[[ATTR2]] // CHECK-NEXT: ret void // void test3e(void) { @@ -97,8 +107,10 @@ // CHECK-LABEL: @t1( // CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.set.rounding(i32 2) #[[ATTR2]] // CHECK-NEXT: [[CONV:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double 4.000000e+00, metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: store float [[CONV]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 0), align 4 +// CHECK-NEXT: call void @llvm.set.rounding(i32 1) #[[ATTR2]] // CHECK-NEXT: ret void // void t1(void) { @@ -107,8 +119,10 @@ // CHECK-LABEL: @t2( // CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.set.rounding(i32 2) #[[ATTR2]] // CHECK-NEXT: [[CONV:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double 4.000000e+00, metadata !"round.upward", metadata !"fpexcept.strict") #[[ATTR2]] // CHECK-NEXT: store float [[CONV]], float* getelementptr inbounds ({ float, float }, { float, float }* @cf, i32 0, i32 1), align 4 +// CHECK-NEXT: call void @llvm.set.rounding(i32 1) #[[ATTR2]] // CHECK-NEXT: ret void // void t2(void) { @@ -118,6 +132,7 @@ // CHECK-LABEL: @t91( // CHECK-NEXT: entry: // CHECK-NEXT: [[C:%.*]] = alloca [0 x i8], align 1 +// CHECK-NEXT: call void @llvm.set.rounding(i32 2) #[[ATTR2]] // CHECK-NEXT: br i1 false, label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] // CHECK: cond.true: // CHECK-NEXT: [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float 2.000000e+00, metadata !"fpexcept.strict") #[[ATTR2]] @@ -128,6 +143,7 @@ // CHECK: cond.end: // CHECK-NEXT: [[COND_R:%.*]] = phi double [ [[CONV]], [[COND_TRUE]] ], [ [[CONV1]], [[COND_FALSE]] ] // CHECK-NEXT: [[COND_I:%.*]] = phi double [ 0.000000e+00, [[COND_TRUE]] ], [ 0.000000e+00, [[COND_FALSE]] ] +// CHECK-NEXT: call void @llvm.set.rounding(i32 1) #[[ATTR2]] // CHECK-NEXT: ret void // void t91(void) { @@ -140,6 +156,7 @@ // CHECK-LABEL: @t92( // CHECK-NEXT: entry: // CHECK-NEXT: [[C:%.*]] = alloca [0 x i8], align 1 +// CHECK-NEXT: call void @llvm.set.rounding(i32 2) #[[ATTR2]] // CHECK-NEXT: br i1 false, label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] // CHECK: cond.true: // CHECK-NEXT: [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float 2.000000e+00, metadata !"fpexcept.strict") #[[ATTR2]] @@ -150,6 +167,7 @@ // CHECK: cond.end: // CHECK-NEXT: [[COND_R:%.*]] = phi double [ [[CONV]], [[COND_TRUE]] ], [ [[CONV1]], [[COND_FALSE]] ] // CHECK-NEXT: [[COND_I:%.*]] = phi double [ 0.000000e+00, [[COND_TRUE]] ], [ 0.000000e+00, [[COND_FALSE]] ] +// CHECK-NEXT: call void @llvm.set.rounding(i32 1) #[[ATTR2]] // CHECK-NEXT: ret void // void t92(void) { diff --git a/clang/test/CodeGen/pragma-fenv_round.c b/clang/test/CodeGen/pragma-fenv_round.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/pragma-fenv_round.c @@ -0,0 +1,205 @@ +// RUN: %clang_cc1 -S -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s + +float default_mode = 1.0f / 3.0f; +// CHECK: @default_mode = global float 0x3FD5555560000000 + +#pragma STDC FENV_ROUND FE_UPWARD +float upward = 1.0f / 3.0f; +// CHECK: @upward = global float 0x3FD5555560000000 + +#pragma STDC FENV_ROUND FE_DOWNWARD +float downward = 1.0f / 3.0f; +// CHECK: @downward = global float 0x3FD5555540000000 + +#pragma STDC FENV_ROUND FE_DYNAMIC +float dynamic = 1.0f / 3.0f; +// CHECK: @dynamic = global float 0x3FD5555560000000 + +#pragma STDC FENV_ROUND FE_TONEAREST + + +float func_1(float w, float x, float y, float z) { + #pragma STDC FENV_ROUND FE_TOWARDZERO + float result = x * y; + { + #pragma STDC FENV_ROUND FE_UPWARD + result += z; + } + return result - w; +} + +// CHECK-LABEL: @func_1 +// CHECK: call void @llvm.set.rounding(i32 0) +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 2) +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 0) +// CHECK: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + + +float func_2(float w, float x, float y, float z) { + #pragma STDC FENV_ROUND FE_TOWARDZERO + float result = x * y; + { + #pragma STDC FENV_ROUND FE_TOWARDZERO + result += z; + } + return result - w; +} + +// CHECK-LABEL: @func_2 +// CHECK: call void @llvm.set.rounding(i32 0) +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + + +float func_3(float w, float x, float y, float z) { + #pragma STDC FENV_ROUND FE_TOWARDZERO + float result = x * y; + { + #pragma STDC FENV_ROUND FE_DYNAMIC + result += z; + } + return result - w; +} + +// CHECK-LABEL: @func_3 +// CHECK: call void @llvm.set.rounding(i32 0) +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.tonearest", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 0) +// CHECK: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + + +float func_4(float w, float x, float y, float z) { + #pragma STDC FENV_ROUND FE_TOWARDZERO + float result = x * y; + { + #pragma STDC FENV_ROUND FE_DYNAMIC + #pragma STDC FENV_ACCESS ON + result += z; + } + return result - w; +} + +// CHECK-LABEL: @func_4 +// CHECK: call void @llvm.set.rounding(i32 0) +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK: call void @llvm.set.rounding(i32 0) +// CHECK: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + + +float func_5(float w, float x, float y, float z) { + #pragma STDC FENV_ROUND FE_DYNAMIC + #pragma STDC FENV_ACCESS ON + #pragma clang fp exceptions(ignore) + float result = x * y; + { + #pragma STDC FENV_ROUND FE_UPWARD + result += z; + } + return result - w; +} + +// CHECK-LABEL: @func_5 +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK: [[RM:%[0-9]+]] = call i32 @llvm.flt.rounds() +// CHECK: call void @llvm.set.rounding(i32 2) +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 [[RM]]) +// CHECK: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + + +float func_6(float w, float x, float y, float z) { + #pragma STDC FENV_ROUND FE_DYNAMIC + #pragma STDC FENV_ACCESS ON + #pragma clang fp exceptions(ignore) + float result = x * y; + { + #pragma STDC FENV_ROUND FE_DYNAMIC + result += z; + } + return result - w; +} + +// CHECK-LABEL: @func_6 +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + + +#pragma STDC FENV_ROUND FE_DOWNWARD +float func_20(float x, float y) { + return x + y; +} + +// CHECK-LABEL: func_20 +// CHECK: call void @llvm.set.rounding(i32 3) +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + +float func_21(float x, float y) { + return x - y; +} + +// CHECK-LABEL: func_21 +// CHECK: call void @llvm.set.rounding(i32 3) +// CHECK: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + +#pragma STDC FENV_ROUND FE_DOWNWARD +float func_22(int x) { + return x; +} + +// CHECK-LABEL: @func_22 +// CHECK: call void @llvm.set.rounding(i32 3) +// CHECK: call float @llvm.experimental.constrained.sitofp.f32.i32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + + +#pragma STDC FENV_ROUND FE_DOWNWARD +float func_23(float w, float x, float y, float z) { + #pragma STDC FENV_ROUND FE_TOWARDZERO + float result = x * y; + { + result += z; + } + return result - w; +} + +// CHECK-LABEL: @func_23 +// CHECK: call void @llvm.set.rounding(i32 0) +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + +float func_24(float w, float x, float y, float z) { + #pragma STDC FENV_ACCESS ON + #pragma STDC FENV_ROUND FE_DYNAMIC + float result = x * y; + { + #pragma STDC FENV_ROUND FE_TOWARDZERO + result += z; + } + return result - w; +} + +// CHECK-LABEL: func_24 +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK: [[RM:%.*]] = call i32 @llvm.flt.rounds() +// CHECK: call void @llvm.set.rounding(i32 0) +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.strict") +// CHECK: call void @llvm.set.rounding(i32 [[RM]]) +// CHECK: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK: call void @llvm.set.rounding(i32 1) diff --git a/clang/test/CodeGen/pragma-fenv_round.cpp b/clang/test/CodeGen/pragma-fenv_round.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/pragma-fenv_round.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -S -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s + +#pragma STDC FENV_ROUND FE_DOWNWARD + +float func_01(float x, float y, float z = 1.0f/3.0f) { + return x + y + z; +} +// CHECK-LABEL: @_Z7func_01fff +// CHECK: call void @llvm.set.rounding(i32 3) +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + +float func_02() { +#pragma STDC FENV_ROUND FE_UPWARD + return func_01(1.0f, 2.0f); +} +// CHECK-LABEL: @_Z7func_02v +// CHECK: call void @llvm.set.rounding(i32 2) +// CHECK: call float @llvm.experimental.constrained.fdiv.f32(float 1.000000e+00, float 3.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore") +// CHECK: call noundef float @_Z7func_01fff(float noundef 1.000000e+00, float noundef 2.000000e+00, float noundef {{.*}}) +// CHECK: call void @llvm.set.rounding(i32 1) + +float func_03(float x, float y, float z = 1.0f/3.0f) { +#pragma STDC FENV_ROUND FE_TOWARDZERO + return x + y + z; +} +// CHECK-LABEL: @_Z7func_03fff +// CHECK: call void @llvm.set.rounding(i32 0) +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call void @llvm.set.rounding(i32 1) + +float func_04() { +#pragma STDC FENV_ROUND FE_UPWARD + return func_01(1.0f, 2.0f); +} +// CHECK-LABEL: @_Z7func_04v +// CHECK: call void @llvm.set.rounding(i32 2) +// CHECK: call float @llvm.experimental.constrained.fdiv.f32(float 1.000000e+00, float 3.000000e+00, metadata !"round.downward", metadata !"fpexcept.ignore") +// CHECK: call noundef float @_Z7func_01fff(float noundef 1.000000e+00, float noundef 2.000000e+00, float noundef {{.*}}) +// CHECK: call void @llvm.set.rounding(i32 1) diff --git a/clang/test/Parser/pragma-fenv_round.c b/clang/test/Parser/pragma-fenv_round.c --- a/clang/test/Parser/pragma-fenv_round.c +++ b/clang/test/Parser/pragma-fenv_round.c @@ -6,6 +6,5 @@ if (x) return y + 2; #pragma STDC FENV_ROUND FE_DOWNWARD // expected-error{{'#pragma STDC FENV_ROUND' can only appear at file scope or at the start of a compound statement}} - // expected-warning@-1{{pragma STDC FENV_ROUND is not supported}} return x + y; }