diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -2322,8 +2322,9 @@ return getStoredFPFeatures().applyOverrides(LO); return FPOptions::defaultWithoutTrailingStorage(LO); } - FPOptionsOverride getFPOptionsOverride() const { - if (UnaryOperatorBits.HasFPFeatures) + + FPOptionsOverride getFPFeatures() const { + if (hasStoredFPFeatures()) return getStoredFPFeatures(); return FPOptionsOverride(); } 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 @@ -1458,6 +1458,12 @@ return *getTrailingObjects(); } + FPOptionsOverride getFPFeatures() const { + if (hasStoredFPFeatures()) + return getStoredFPFeatures(); + return FPOptionsOverride(); + } + /// Get FPOptions inside this statement. They may differ from outer options /// due to pragmas. /// \param CurFPOptions FPOptions outside this statement. 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 @@ -1570,6 +1570,7 @@ FPFeaturesStateRAII(Sema &S); ~FPFeaturesStateRAII(); FPOptionsOverride getOverrides() { return OldOverrides; } + FPOptions getFPFeatures() { return OldFPFeaturesState; } private: Sema& S; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -7177,10 +7177,10 @@ if (Err) return std::move(Err); - return UnaryOperator::Create( - Importer.getToContext(), ToSubExpr, E->getOpcode(), ToType, - E->getValueKind(), E->getObjectKind(), ToOperatorLoc, E->canOverflow(), - E->getFPOptionsOverride()); + return UnaryOperator::Create(Importer.getToContext(), ToSubExpr, + E->getOpcode(), ToType, E->getValueKind(), + E->getObjectKind(), ToOperatorLoc, + E->canOverflow(), E->getFPFeatures()); } ExpectedStmt diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5049,7 +5049,8 @@ Sema::ContextRAII savedContext(*this, Function); FPFeaturesStateRAII SavedFPFeatures(*this); - CurFPFeatures = FPOptions(getLangOpts()); + if (TSK != TSK_ExplicitInstantiationDefinition) + CurFPFeatures = FPOptions(getLangOpts()); if (addInstantiatedParametersToScope(Function, PatternDecl, Scope, TemplateArgs)) 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 @@ -3814,6 +3814,18 @@ QualType TransformDependentNameType(TypeLocBuilder &TLB, DependentNameTypeLoc TL, bool DeducibleTSTContext); + + /// Sets FP options in Sema for use in instantiation of the given expression. + /// + /// \tparam TExpr Subclass of \c Expr, which must be able to keep \c FPOption. + template void setFPOptions(const TExpr *E) { + const LangOptions &LO = getSema().getLangOpts(); + FPOptions &CurFPO = getSema().CurFPFeatures; + CurFPO = E->getFPFeatures().applyOverrides(CurFPO); + getSema().FpPragmaStack.CurrentValue = CurFPO.getChangesFrom(FPOptions(LO)); + if (CurFPO.isFPConstrained()) + getSema().getCurFunction()->setUsesFPIntrin(); + } }; template @@ -3910,9 +3922,6 @@ while (CXXBindTemporaryExpr *Binder = dyn_cast(Init)) Init = Binder->getSubExpr(); - if (ImplicitCastExpr *ICE = dyn_cast(Init)) - Init = ICE->getSubExprAsWritten(); - if (CXXStdInitializerListExpr *ILE = dyn_cast(Init)) return TransformInitializer(ILE->getSubExpr(), NotCopyInit); @@ -7300,6 +7309,10 @@ bool SubStmtInvalid = false; bool SubStmtChanged = false; SmallVector Statements; + + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); + setFPOptions(S); + for (auto *B : S->body()) { StmtResult Result = getDerived().TransformStmt( B, IsStmtExpr && B == ExprResult ? SDK_StmtExprResult : SDK_Discarded); @@ -7322,8 +7335,8 @@ if (SubStmtInvalid) return StmtError(); - if (!getDerived().AlwaysRebuild() && - !SubStmtChanged) + if (!getDerived().AlwaysRebuild() && !SubStmtChanged && + FPFeaturesState.getFPFeatures() == getSema().CurFPFeatures) return S; return getDerived().RebuildCompoundStmt(S->getLBracLoc(), @@ -10693,7 +10706,11 @@ if (SubExpr.isInvalid()) return ExprError(); - if (!getDerived().AlwaysRebuild() && SubExpr.get() == E->getSubExpr()) + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); + setFPOptions(E); + + if (!getDerived().AlwaysRebuild() && SubExpr.get() == E->getSubExpr() && + FPFeaturesState.getFPFeatures() == getSema().CurFPFeatures) return E; return getDerived().RebuildUnaryOperator(E->getOperatorLoc(), @@ -11054,23 +11071,17 @@ &ArgChanged)) return ExprError(); - if (!getDerived().AlwaysRebuild() && - Callee.get() == E->getCallee() && - !ArgChanged) + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); + setFPOptions(E); + + if (!getDerived().AlwaysRebuild() && Callee.get() == E->getCallee() && + !ArgChanged && FPFeaturesState.getFPFeatures() == getSema().CurFPFeatures) return SemaRef.MaybeBindToTemporary(E); // FIXME: Wrong source location information for the '('. SourceLocation FakeLParenLoc = ((Expr *)Callee.get())->getSourceRange().getBegin(); - Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); - if (E->hasStoredFPFeatures()) { - FPOptionsOverride NewOverrides = E->getFPFeatures(); - getSema().CurFPFeatures = - NewOverrides.applyOverrides(getSema().getLangOpts()); - getSema().FpPragmaStack.CurrentValue = NewOverrides; - } - return getDerived().RebuildCallExpr(Callee.get(), FakeLParenLoc, Args, E->getRParenLoc()); @@ -11176,20 +11187,20 @@ if (RHS.isInvalid()) return ExprError(); - if (!getDerived().AlwaysRebuild() && - LHS.get() == E->getLHS() && - RHS.get() == E->getRHS()) + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); + setFPOptions(E); + + if (!getDerived().AlwaysRebuild() && LHS.get() == E->getLHS() && + RHS.get() == E->getRHS() && + FPFeaturesState.getFPFeatures() == getSema().CurFPFeatures) return E; if (E->isCompoundAssignmentOp()) - // FPFeatures has already been established from trailing storage + // FPFeatures has already been established from trailing storage by + // TransformCompoundAssignOperator. return getDerived().RebuildBinaryOperator( E->getOperatorLoc(), E->getOpcode(), LHS.get(), RHS.get()); - Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); - FPOptionsOverride NewOverrides(E->getFPFeatures()); - getSema().CurFPFeatures = - NewOverrides.applyOverrides(getSema().getLangOpts()); - getSema().FpPragmaStack.CurrentValue = NewOverrides; + return getDerived().RebuildBinaryOperator(E->getOperatorLoc(), E->getOpcode(), LHS.get(), RHS.get()); } @@ -11253,10 +11264,7 @@ TreeTransform::TransformCompoundAssignOperator( CompoundAssignOperator *E) { Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); - FPOptionsOverride NewOverrides(E->getFPFeatures()); - getSema().CurFPFeatures = - NewOverrides.applyOverrides(getSema().getLangOpts()); - getSema().FpPragmaStack.CurrentValue = NewOverrides; + setFPOptions(E); return getDerived().TransformBinaryOperator(E); } @@ -11317,9 +11325,50 @@ template ExprResult TreeTransform::TransformImplicitCastExpr(ImplicitCastExpr *E) { + ExprResult SubExpr = getDerived().TransformExpr(E->getSubExprAsWritten()); + if (SubExpr.isInvalid()) + return ExprError(); + // Implicit casts are eliminated during transformation, since they // will be recomputed by semantic analysis after transformation. - return getDerived().TransformExpr(E->getSubExprAsWritten()); + // If however the cast involves floating-point transformations, it may keep + // FP options with it. In this case the implicit cast must be processed here, + // because semantic actions made latter have no access to the FP options. + switch (E->getCastKind()) { + default: + return SubExpr; + case CastKind::CK_FloatingCast: + case CastKind::CK_FloatingComplexCast: + case CastKind::CK_FloatingComplexToIntegralComplex: + case CastKind::CK_IntegralComplexToFloatingComplex: + case CastKind::CK_IntegralToFloating: + case CastKind::CK_FloatingToIntegral: + case CastKind::CK_FloatingToBoolean: + case CastKind::CK_FloatingComplexToBoolean: + break; + } + + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); + setFPOptions(E); + + if (FPFeaturesState.getFPFeatures() == getSema().CurFPFeatures) + return SubExpr; + + if (SubExpr.get()->isLValue()) + SubExpr = ImplicitCastExpr::Create( + getSema().getASTContext(), + SubExpr.get()->getType().getNonReferenceType(), CK_LValueToRValue, + SubExpr.get(), nullptr, VK_PRValue, getSema().CurFPFeatureOverrides()); + if (SubExpr.isInvalid()) + return ExprError(); + + QualType T = getDerived().TransformType(E->getType()); + if (T.isNull()) + return ExprError(); + + return ImplicitCastExpr::Create( + getSema().getASTContext(), T, E->getCastKind(), SubExpr.get(), nullptr, + VK_PRValue, getSema().CurFPFeatureOverrides()); } template @@ -11334,9 +11383,12 @@ if (SubExpr.isInvalid()) return ExprError(); - if (!getDerived().AlwaysRebuild() && - Type == E->getTypeInfoAsWritten() && - SubExpr.get() == E->getSubExpr()) + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); + setFPOptions(E); + + if (!getDerived().AlwaysRebuild() && Type == E->getTypeInfoAsWritten() && + SubExpr.get() == E->getSubExpr() && + FPFeaturesState.getFPFeatures() == getSema().CurFPFeatures) return E; return getDerived().RebuildCStyleCastExpr(E->getLParenLoc(), @@ -11728,18 +11780,15 @@ return ExprError(); } - if (!getDerived().AlwaysRebuild() && - Callee.get() == E->getCallee() && + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); + setFPOptions(E); + + if (!getDerived().AlwaysRebuild() && Callee.get() == E->getCallee() && First.get() == E->getArg(0) && - (E->getNumArgs() != 2 || Second.get() == E->getArg(1))) + (E->getNumArgs() != 2 || Second.get() == E->getArg(1)) && + FPFeaturesState.getFPFeatures() == getSema().CurFPFeatures) return SemaRef.MaybeBindToTemporary(E); - Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); - FPOptionsOverride NewOverrides(E->getFPFeatures()); - getSema().CurFPFeatures = - NewOverrides.applyOverrides(getSema().getLangOpts()); - getSema().FpPragmaStack.CurrentValue = NewOverrides; - return getDerived().RebuildCXXOperatorCallExpr(E->getOperator(), E->getOperatorLoc(), Callee.get(), @@ -11811,10 +11860,14 @@ if (SubExpr.isInvalid()) return ExprError(); - if (!getDerived().AlwaysRebuild() && - Type == E->getTypeInfoAsWritten() && - SubExpr.get() == E->getSubExpr()) + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); + setFPOptions(E); + + if (!getDerived().AlwaysRebuild() && Type == E->getTypeInfoAsWritten() && + SubExpr.get() == E->getSubExpr() && + FPFeaturesState.getFPFeatures() == getSema().CurFPFeatures) return E; + return getDerived().RebuildCXXNamedCastExpr( E->getOperatorLoc(), E->getStmtClass(), E->getAngleBrackets().getBegin(), Type, E->getAngleBrackets().getEnd(), @@ -11883,9 +11936,12 @@ if (SubExpr.isInvalid()) return ExprError(); - if (!getDerived().AlwaysRebuild() && - Type == E->getTypeInfoAsWritten() && - SubExpr.get() == E->getSubExpr()) + Sema::FPFeaturesStateRAII FPFeaturesState(getSema()); + setFPOptions(E); + + if (!getDerived().AlwaysRebuild() && Type == E->getTypeInfoAsWritten() && + SubExpr.get() == E->getSubExpr() && + FPFeaturesState.getFPFeatures() == getSema().CurFPFeatures) return E; return getDerived().RebuildCXXFunctionalCastExpr(Type, diff --git a/clang/test/CodeGen/fp-pragma-template.cpp b/clang/test/CodeGen/fp-pragma-template.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/fp-pragma-template.cpp @@ -0,0 +1,82 @@ +// RUN: %clang_cc1 -S -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s + + +// If a pragma is used inside template, it has effect on all instantiations. + +template +float func_01(float x, float y) { +#pragma STDC FENV_ROUND FE_DOWNWARD + return x + y; +} + +#pragma STDC FENV_ROUND FE_TONEAREST +template float func_01(float, float); +// CHECK-LABEL: define {{.*}}float @_Z7func_01IsEfff(float {{.*}}, float {{.*}}) {{.*}}{ +// CHECK: float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore") + +float func_02(float x, float y) { + return func_01(x, y); +} +// CHECK-LABEL: define {{.*}}float @_Z7func_02ff(float {{.*}}, float {{.*}}) {{.*}}{ +// CHECK: call {{.*}}float @_Z7func_01IlEfff( + +// CHECK-LABEL: define {{.*}}float @_Z7func_01IlEfff(float {{.*}}, float {{.*}}) {{.*}}{ +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore") + +template +float func_03(float x, float y, float z) { +#pragma STDC FENV_ROUND FE_TOWARDZERO + float res = 3.0F * x; + { + #pragma STDC FENV_ROUND FE_UPWARD + res += y; + } + return res - z; +} + +#pragma STDC FENV_ROUND FE_DOWNWARD +template float func_03(float, float, float); +// CHECK-LABEL: define {{.*}}float @_Z7func_03IsEffff(float {{.*}}, float {{.*}}) {{.*}}{ +// CHECK: call float @llvm.experimental.constrained.fmul.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.ignore") +// CHECK: call float @llvm.experimental.constrained.fsub.f32({{.*}}, metadata !"round.towardzero", metadata !"fpexcept.ignore") + + +// A pragma at file level has the same effect as if the same pragma were +// specified at the start of each affected function. + +#pragma STDC FENV_ROUND FE_UPWARD +template +float func_04(float x, float y) { + return x + y; +} + +#pragma STDC FENV_ROUND FE_TONEAREST +template float func_04(float, float); +// CHECK-LABEL: define {{.*}}float @_Z7func_04IsEfff(float {{.*}}, float {{.*}}) {{.*}}{ +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.upward", metadata !"fpexcept.ignore") + + +// Explicit instantiation inherits FP features of the point of instantiation, +// unless they are explicitly overridden by pragmas in the template. + +#pragma STDC FENV_ROUND FE_DYNAMIC +template +float func_05(float x, float y) { + return x + y; +} + +#pragma STDC FENV_ROUND FE_DOWNWARD +template float func_05(float, float); +// CHECK-LABEL: define {{.*}}float @_Z7func_05IsEfff(float {{.*}}, float {{.*}}) {{.*}}{ +// CHECK: call float @llvm.experimental.constrained.fadd.f32({{.*}}, metadata !"round.downward", metadata !"fpexcept.ignore") + +#pragma STDC FENV_ROUND FE_DYNAMIC +float func_06(float x, float y) { + return func_05(x, y); +} +// CHECK-LABEL: define {{.*}}float @_Z7func_06ff(float {{.*}}, float {{.*}}) {{.*}}{ +// CHECK: call {{.*}}float @_Z7func_05IlEfff( + +// CHECK-LABEL: define {{.*}}float @_Z7func_05IlEfff(float {{.*}}, float {{.*}}) {{.*}}{ +// CHECK: fadd