diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -1457,6 +1457,13 @@ return *getTrailingObjects(); } + bool changesFPControlModes() const { + return hasStoredFPFeatures() && + getStoredFPFeatures().hasRoundingModeOverride() && + getStoredFPFeatures().getRoundingModeOverride() != + llvm::RoundingMode::Dynamic; + } + using body_iterator = Stmt **; using body_range = llvm::iterator_range; 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 @@ -1189,9 +1189,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 @@ -452,6 +452,46 @@ 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(); + if (!FPO.hasRoundingModeOverride() || + FPO.getRoundingModeOverride() == llvm::RoundingMode::Dynamic) + return; + FPOptions FP = FPO.applyOverrides(getLangOpts()); + llvm::RoundingMode OldRM = CurFPFeatures.getRoundingMode(); + llvm::RoundingMode NewRM = FP.getRoundingMode(); + llvm::Value *PreviousRM = nullptr; + if (OldRM == llvm::RoundingMode::Dynamic) { + llvm::Function *FGetRound = CGM.getIntrinsic(llvm::Intrinsic::flt_rounds); + PreviousRM = Builder.CreateCall(FGetRound); + } else if (NewRM == llvm::RoundingMode::Dynamic || NewRM != OldRM) { + PreviousRM = llvm::ConstantInt::get(Int32Ty, static_cast(OldRM)); + } else { + return; + } + 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). @@ -475,6 +515,14 @@ 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.changesFPControlModes() + ? S.getStoredFPFeatures().applyOverrides(CurFPFeatures) + : CurFPFeatures); + Address RetAlloca = Address::invalid(); for (auto *CurStmt : S.body()) { 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 @@ -3021,6 +3021,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 @@ -3228,9 +3228,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 @@ -5,8 +5,6 @@ // Test that the constrained intrinsics are picking up the exception // metadata from the AST instead of the global default from the command line. // Include rounding metadata in the testing. -// FIXME: All cases of "fpexcept.maytrap" in this test are wrong. -// FIXME: All cases of "round.tonearest" in this test are wrong. #pragma float_control(except, on) #pragma STDC FENV_ROUND FE_UPWARD @@ -17,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) { @@ -35,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]] @@ -42,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) { @@ -50,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 @@ -63,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) { @@ -71,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.tonearest", metadata !"fpexcept.maytrap") #[[ATTR2]] +// 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) { @@ -85,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.tonearest", metadata !"fpexcept.maytrap") #[[ATTR2]] +// 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) { @@ -99,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) { @@ -109,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) { @@ -120,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]] @@ -130,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) { @@ -142,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]] @@ -152,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,98 @@ +// RUN: %clang_cc1 -S -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s + +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) + + +#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) + + +#pragma STDC FENV_ROUND FE_DOWNWARD +float func_21(int x) { + return x; +} + +// CHECK-LABEL: @func_21 +// 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_22(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_22 +// 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) 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; }