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 @@ -1116,9 +1116,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 @@ -4120,6 +4120,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 @@ -913,8 +913,10 @@ Fn->addFnAttr(llvm::Attribute::NoRecurse); if (const FunctionDecl *FD = dyn_cast_or_null(D)) - if (FD->usesFPIntrin()) + if (FD->usesFPIntrin()) { Fn->addFnAttr(llvm::Attribute::StrictFP); + Builder.setIsFPConstrained(true); + } // If a custom alignment is used, force realigning to this alignment on // any main function which certainly will need it. 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 @@ -1660,6 +1660,15 @@ } } +void CodeGenModule::setLLVMFunctionFEnvAttributes(const FunctionDecl *D, + llvm::Function *F) { + if (D->usesFPIntrin()) { + 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)) @@ -4470,9 +4479,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 FPFeatures, restore on leaving the + // Record the current FPFeatures, restore on leaving the // compound statement. - Sema::FPFeaturesStateRAII 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->setUsesFPIntrin(true); + } +} + sema::CompoundScopeInfo &Sema::getCurCompoundScope() const { return getCurFunction()->CompoundScopes.back(); } 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