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 @@ -3420,3 +3420,11 @@ let Subjects = SubjectList<[NonParmVar, Function, Block, ObjCMethod]>; let Documentation = [ObjCExternallyRetainedDocs]; } + +def FPEnvironment : 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 @@ -1060,8 +1060,12 @@ // - #pragma stdc unknown def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">, 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">; def warn_stdc_fenv_access_not_supported : - Warning<"pragma STDC FENV_ACCESS ON is not supported, ignoring pragma">, + Warning<"in functions '#pragma STDC FENV_ACCESS ON' is supported only " + "in topmost block, ignoring pragma">, InGroup; // - #pragma comment def err_pragma_comment_malformed : Error< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -885,6 +885,9 @@ def err_pragma_attr_attr_no_push : Error< "'#pragma clang attribute' attribute with no matching " "'#pragma clang attribute push'">; +def warn_pragma_fenv_access_cannot_apply : Warning<"'#pragma STDC FENV_ACCESS ON' " + "cannot apply to inline functions, ignoring pragma">, + InGroup; /// Objective-C parser diagnostics def err_duplicate_class_def : Error< 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 @@ -1259,12 +1259,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; @@ -3986,6 +3986,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 { @@ -8963,7 +8964,8 @@ /// ActOnPragmaFenvAccess - Called on well formed /// \#pragma STDC FENV_ACCESS - void ActOnPragmaFEnvAccess(LangOptions::FEnvAccessModeKind FPC); + void ActOnPragmaFEnvAccess(LangOptions::FEnvAccessModeKind FPC, + SourceLocation DiagLoc); /// AddAlignmentAttributesForRecord - Adds any needed alignment attributes to /// a the record decl, to handle '\#pragma pack' and '\#pragma options align'. 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 @@ -827,6 +827,10 @@ CGM.getCodeGenOpts().StackAlignment) Fn->addFnAttr("stackrealign"); + if (const FunctionDecl *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 @@ -1092,6 +1092,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 @@ -1632,6 +1632,15 @@ } } +void CodeGenModule::setLLVMFunctionFEnvAttributes(const FunctionDecl *D, + llvm::Function *F) { + if (D->hasAttr()) { + llvm::AttrBuilder FuncAttrs; + FuncAttrs.addAttribute("fenv_access"); + 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)) @@ -4406,9 +4415,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); @@ -641,7 +638,12 @@ break; } - Actions.ActOnPragmaFEnvAccess(FPC); + if (Actions.getCurFunction() && !Actions.getCurScope()->isFunctionScope()) { + PP.Diag(Tok, diag::warn_stdc_fenv_access_not_supported); + FPC = LangOptions::FEA_Off; + } + + Actions.ActOnPragmaFEnvAccess(FPC, Tok.getLocation()); ConsumeAnnotationToken(); } 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/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -940,9 +940,18 @@ } } -void Sema::ActOnPragmaFEnvAccess(LangOptions::FEnvAccessModeKind FPC) { +void Sema::ActOnPragmaFEnvAccess(LangOptions::FEnvAccessModeKind FPC, + SourceLocation DiagLoc) { switch (FPC) { case LangOptions::FEA_On: + if (FunctionDecl *FD = getCurFunctionDecl()) { + if (FD->isInlined() || FD->hasAttr() || + FD->hasAttr()) { + Diag(DiagLoc, diag::warn_pragma_fenv_access_cannot_apply); + FPFeatures.setDisallowFEnvAccess(); + break; + } + } FPFeatures.setAllowFEnvAccess(); break; case LangOptions::FEA_Off: 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,21 @@ PopCompoundScope(); } +void Sema::ActOnAfterCompoundStatementLeadingPragmas() { + if (FPFeatures.allowFEnvAccess() && getCurScope()->isFunctionScope()) { + FunctionDecl *F = getCurFunctionDecl(); + if (F->isInlined() || F->hasAttr() || + F->hasAttr()) { + Diag(F->getNameInfo().getLoc(), + diag::warn_pragma_fenv_access_cannot_apply); + return; + } + + F->addAttr(NoInlineAttr::CreateImplicit(Context)); + F->addAttr(FPEnvironmentAttr::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 @@ -9773,7 +9773,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(), @@ -10299,7 +10299,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,37 @@ +// 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") 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,37 @@ +// 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 ON + +inline float func_01(float x) { // expected-warning {{'#pragma STDC FENV_ACCESS ON' cannot apply to inline functions, ignoring pragma}} + return x + 2; +} + +inline float func_02(float x) { + #pragma STDC FENV_ACCESS OFF + return x + 2; +} + +#pragma STDC FENV_ACCESS OFF + +inline float func_03(float x) { + #pragma STDC FENV_ACCESS ON // expected-warning {{'#pragma STDC FENV_ACCESS ON' cannot apply to inline functions}} + return x + 2; +} + +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; +} + +float func_05(int x, float y) { + if (x) { + #pragma STDC FENV_ACCESS ON // expected-warning {{in functions '#pragma STDC FENV_ACCESS ON' is supported only in topmost block}} + return y + 2; + } + return 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