sepavloff created this revision.
sepavloff added reviewers: rsmith, rjmccall, aaron.ballman, efriedma.
Herald added a subscriber: martong.
Herald added a reviewer: shafik.
Herald added a project: All.
sepavloff requested review of this revision.
Herald added a project: clang.

C standard defines set of pragmas that modify code generation for
operations on floating point values. In C++ code may be obtained using
template instantiation and from practical viewpoint it is necessary to
have possibility to modify code generation in this case as well. This
change implements interaction of FP pragmas with function template
instantiations.

If a function template contains a floating-point control pragma, the
latter is a part of template definition and presents in all
instantiations. If a pragma in placed outside any function it acts on
function templates in the same way as on functions. For the task of
function instantiation such pragma is equivalent to the same pragma put
at the beginning of all affected function bodies.

In the case is explicit instantiation there is apparent connection
between a point in source code and instantiated function. It can
support interaction of pragmas that act in that point. For example, in
the code:

  #pragma STDC FENV_ROUND FE_DOWNWARD
  template float func_05<short>(float, float);

the instantiated function is created with the specified constant
rounding mode. The effect is same as if the template pattern were
textually inserted into the code with needed replacements.

Implicit instantiations occur with FP options deduced from LangOpts.
They have no apparent connection to source code and are not influenced
by FP pragmas.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131143

Files:
  clang/include/clang/AST/Expr.h
  clang/include/clang/AST/Stmt.h
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/ASTImporter.cpp
  clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
  clang/lib/Sema/TreeTransform.h
  clang/test/CodeGen/fp-pragma-template.cpp

Index: clang/test/CodeGen/fp-pragma-template.cpp
===================================================================
--- /dev/null
+++ 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 <typename Ty>
+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<short>(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<long>(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 <typename Ty>
+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<short>(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 <typename Ty>
+float func_04(float x, float y) {
+  return x + y;
+}
+
+#pragma STDC FENV_ROUND FE_TONEAREST
+template float func_04<short>(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 <typename Ty>
+float func_05(float x, float y) {
+  return x + y;
+}
+
+#pragma STDC FENV_ROUND FE_DOWNWARD
+template float func_05<short>(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<long>(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
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ 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 <typename TExpr> 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 <typename Derived>
@@ -3910,9 +3922,6 @@
   while (CXXBindTemporaryExpr *Binder = dyn_cast<CXXBindTemporaryExpr>(Init))
     Init = Binder->getSubExpr();
 
-  if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Init))
-    Init = ICE->getSubExprAsWritten();
-
   if (CXXStdInitializerListExpr *ILE =
           dyn_cast<CXXStdInitializerListExpr>(Init))
     return TransformInitializer(ILE->getSubExpr(), NotCopyInit);
@@ -7300,6 +7309,10 @@
   bool SubStmtInvalid = false;
   bool SubStmtChanged = false;
   SmallVector<Stmt*, 8> 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<Derived>::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<typename Derived>
 ExprResult
 TreeTransform<Derived>::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<typename Derived>
@@ -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,
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ 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))
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ 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
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ 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;
Index: clang/include/clang/AST/Stmt.h
===================================================================
--- clang/include/clang/AST/Stmt.h
+++ clang/include/clang/AST/Stmt.h
@@ -1458,6 +1458,12 @@
     return *getTrailingObjects<FPOptionsOverride>();
   }
 
+  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.
Index: clang/include/clang/AST/Expr.h
===================================================================
--- clang/include/clang/AST/Expr.h
+++ 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();
   }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to