diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3495,3 +3495,11 @@ let Subjects = SubjectList<[ParmVar]>; let Documentation = [ReleaseHandleDocs]; } + +def StrictFP : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly. + let Spellings = []; + let Args = [BoolArgument<"Access">]; // TODO: Add Rounding and Exceptions. + let Subjects = SubjectList<[Function]>; + let Documentation = [Undocumented]; +} 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 @@ -1069,9 +1069,9 @@ // - #pragma stdc unknown def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">, InGroup; -def warn_stdc_fenv_access_not_supported : - Warning<"pragma STDC FENV_ACCESS ON is not supported, ignoring pragma">, - InGroup; +def err_pragma_stdc_fenv_access_scope : Error< + "'#pragma STDC FENV_ACCESS' can only appear at file scope or at the start of a " + "compound statement">; // - #pragma comment def err_pragma_comment_malformed : Error< "pragma comment requires parenthesized identifier and optional string">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1286,12 +1286,12 @@ /// should not be used elsewhere. void EmitCurrentDiagnostic(unsigned DiagID); - /// Records and restores the FP_CONTRACT state on entry/exit of compound + /// Records and restores the FPFeatures state on entry/exit of compound /// statements. - class FPContractStateRAII { + class FPFeaturesStateRAII { public: - FPContractStateRAII(Sema &S) : S(S), OldFPFeaturesState(S.FPFeatures) {} - ~FPContractStateRAII() { S.FPFeatures = OldFPFeaturesState; } + FPFeaturesStateRAII(Sema &S) : S(S), OldFPFeaturesState(S.FPFeatures) {} + ~FPFeaturesStateRAII() { S.FPFeatures = OldFPFeaturesState; } private: Sema& S; @@ -4071,6 +4071,7 @@ void ActOnFinishOfCompoundStmt(); StmtResult ActOnCompoundStmt(SourceLocation L, SourceLocation R, ArrayRef Elts, bool isStmtExpr); + void ActOnAfterCompoundStatementLeadingPragmas(); /// A RAII object to enter scope of a compound statement. class CompoundScopeRAII { diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -896,6 +896,10 @@ CGM.getCodeGenOpts().StackAlignment) Fn->addFnAttr("stackrealign"); + if (const auto *FD = dyn_cast_or_null(D)) + if (const auto *FEnv = FD->getAttr()) + Builder.setIsFPConstrained(FEnv->getAccess()); + llvm::BasicBlock *EntryBB = createBasicBlock("entry", CurFn); // Create a marker to make it easy to insert allocas into the entryblock diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1110,6 +1110,10 @@ /// definition. void SetLLVMFunctionAttributesForDefinition(const Decl *D, llvm::Function *F); + /// Set the LLVM function attributes that represent floating point + /// environment. + void setLLVMFunctionFEnvAttributes(const FunctionDecl *D, llvm::Function *F); + /// Return true iff the given type uses 'sret' when used as a return type. bool ReturnTypeUsesSRet(const CGFunctionInfo &FI); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1644,6 +1644,15 @@ } } +void CodeGenModule::setLLVMFunctionFEnvAttributes(const FunctionDecl *D, + llvm::Function *F) { + if (D->hasAttr()) { + llvm::AttrBuilder FuncAttrs; + FuncAttrs.addAttribute("strictfp"); + F->addAttributes(llvm::AttributeList::FunctionIndex, FuncAttrs); + } +} + void CodeGenModule::SetCommonAttributes(GlobalDecl GD, llvm::GlobalValue *GV) { const Decl *D = GD.getDecl(); if (dyn_cast_or_null(D)) @@ -4449,9 +4458,11 @@ MaybeHandleStaticInExternC(D, Fn); - maybeSetTrivialComdat(*D, *Fn); + // Set CodeGen attributes that represent floating point environment. + setLLVMFunctionFEnvAttributes(D, Fn); + CodeGenFunction(*this).GenerateCode(D, Fn, FI); setNonAliasAttributes(GD, Fn); 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 @@ -106,9 +106,6 @@ tok::OnOffSwitch OOS; if (PP.LexOnOffSwitch(OOS)) return; - if (OOS == tok::OOS_ON) { - PP.Diag(Tok, diag::warn_stdc_fenv_access_not_supported); - } MutableArrayRef Toks(PP.getPreprocessorAllocator().Allocate(1), 1); diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -365,7 +365,8 @@ case tok::annot_pragma_fenv_access: ProhibitAttributes(Attrs); - HandlePragmaFEnvAccess(); + Diag(Tok, diag::err_pragma_stdc_fenv_access_scope); + ConsumeAnnotationToken(); return StmtEmpty(); case tok::annot_pragma_opencl_extension: @@ -1014,9 +1015,9 @@ Tok.getLocation(), "in compound statement ('{}')"); - // Record the state of the FP_CONTRACT pragma, restore on leaving the + // Record the current FPFeatures, restore on leaving the // compound statement. - Sema::FPContractStateRAII SaveFPContractState(Actions); + Sema::FPFeaturesStateRAII SaveFPFeatures(Actions); InMessageExpressionRAIIObject InMessage(*this, false); BalancedDelimiterTracker T(*this, tok::l_brace); @@ -1027,6 +1028,7 @@ // Parse any pragmas at the beginning of the compound statement. ParseCompoundStatementLeadingPragmas(); + Actions.ActOnAfterCompoundStatementLeadingPragmas(); StmtVector Stmts; diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -381,6 +381,13 @@ PopCompoundScope(); } +void Sema::ActOnAfterCompoundStatementLeadingPragmas() { + if (FPFeatures.allowFEnvAccess()) { + FunctionDecl *F = getCurFunctionDecl(); + F->addAttr(StrictFPAttr::CreateImplicit(Context, true)); + } +} + sema::CompoundScopeInfo &Sema::getCurCompoundScope() const { return getCurFunction()->CompoundScopes.back(); } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9837,7 +9837,7 @@ RHS.get() == E->getRHS()) return E; - Sema::FPContractStateRAII FPContractState(getSema()); + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); getSema().FPFeatures = E->getFPFeatures(); return getDerived().RebuildBinaryOperator(E->getOperatorLoc(), E->getOpcode(), @@ -10363,7 +10363,7 @@ (E->getNumArgs() != 2 || Second.get() == E->getArg(1))) return SemaRef.MaybeBindToTemporary(E); - Sema::FPContractStateRAII FPContractState(getSema()); + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); getSema().FPFeatures = E->getFPFeatures(); return getDerived().RebuildCXXOperatorCallExpr(E->getOperator(), diff --git a/clang/test/CodeGen/pragma-fenv_access.c b/clang/test/CodeGen/pragma-fenv_access.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/pragma-fenv_access.c @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm %s -o - | FileCheck %s + +#pragma STDC FENV_ACCESS ON + +float func_01(float x, float y) { + return x + y; +} +// CHECK-LABEL: @func_01 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + +float func_02(float x, float y) { + #pragma STDC FENV_ACCESS OFF + return x + y; +} +// CHECK-LABEL: @func_02 +// CHECK: fadd float {{.*}} + + +float func_03(float x, float y) { + return x + y; +} +// CHECK-LABEL: @func_03 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + +#pragma STDC FENV_ACCESS OFF + +float func_04(float x, float y) { + return x + y; +} +// CHECK-LABEL: @func_04 +// CHECK: fadd float {{.*}} + + +float func_05(float x, float y) { + #pragma STDC FENV_ACCESS ON + return x + y; +} +// CHECK-LABEL: @func_05 +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + + +float func_06(float x, float y) { + return x + y; +} +// CHECK-LABEL: @func_06 +// CHECK: fadd float {{.*}} + + +float func_07(float x, float y) { + x -= y; + if (x) { + #pragma STDC FENV_ACCESS ON + y *= 2; + } + return y + 4; +} +// CHECK-LABEL: @func_07 +// CHECK: call float @llvm.experimental.constrained.fsub.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK: call float @llvm.experimental.constrained.fmul.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") +// CHECK: call float @llvm.experimental.constrained.fadd.f32(float {{.*}}, float {{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") diff --git a/clang/test/Parser/pragma-fenv_access.c b/clang/test/Parser/pragma-fenv_access.c new file mode 100644 --- /dev/null +++ b/clang/test/Parser/pragma-fenv_access.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#pragma STDC FENV_ACCESS IN_BETWEEN // expected-warning {{expected 'ON' or 'OFF' or 'DEFAULT' in pragma}} + +#pragma STDC FENV_ACCESS OFF + +float func_04(int x, float y) { + if (x) + return y + 2; + #pragma STDC FENV_ACCESS ON // expected-error{{'#pragma STDC FENV_ACCESS' can only appear at file scope or at the start of a compound statement}} + return x + y; +} diff --git a/clang/test/Preprocessor/pragma_unknown.c b/clang/test/Preprocessor/pragma_unknown.c --- a/clang/test/Preprocessor/pragma_unknown.c +++ b/clang/test/Preprocessor/pragma_unknown.c @@ -16,15 +16,6 @@ // CHECK: {{^}}#pragma STDC FP_CONTRACT DEFAULT{{$}} // CHECK: {{^}}#pragma STDC FP_CONTRACT IN_BETWEEN{{$}} -#pragma STDC FENV_ACCESS ON // expected-warning {{pragma STDC FENV_ACCESS ON is not supported, ignoring pragma}} -#pragma STDC FENV_ACCESS OFF -#pragma STDC FENV_ACCESS DEFAULT -#pragma STDC FENV_ACCESS IN_BETWEEN // expected-warning {{expected 'ON' or 'OFF' or 'DEFAULT' in pragma}} -// CHECK: {{^}}#pragma STDC FENV_ACCESS ON{{$}} -// CHECK: {{^}}#pragma STDC FENV_ACCESS OFF{{$}} -// CHECK: {{^}}#pragma STDC FENV_ACCESS DEFAULT{{$}} -// CHECK: {{^}}#pragma STDC FENV_ACCESS IN_BETWEEN{{$}} - #pragma STDC CX_LIMITED_RANGE ON #pragma STDC CX_LIMITED_RANGE OFF #pragma STDC CX_LIMITED_RANGE DEFAULT