kpn created this revision.
kpn added a reviewer: rsmith.
Herald added a reviewer: javed.absar.
Herald added a subscriber: cfe-commits.

This builds on https://reviews.llvm.org/D49865 to get #pragma STDC FENV_ACCESS 
ON to and used by AST handling of function calls to math intrinsics. This now 
emits constrained math intrinsics instead of the regular variety for the ones 
currently implemented in llvm.

This should be good for C. More work is needed for C++.


Repository:
  rC Clang

https://reviews.llvm.org/D51372

Files:
  include/clang/AST/Expr.h
  include/clang/AST/ExprCXX.h
  lib/AST/ASTImporter.cpp
  lib/AST/Expr.cpp
  lib/Analysis/BodyFarm.cpp
  lib/CodeGen/CGBuiltin.cpp
  lib/Frontend/Rewrite/RewriteModernObjC.cpp
  lib/Frontend/Rewrite/RewriteObjC.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaOpenMP.cpp
  lib/Sema/SemaOverload.cpp
  lib/Sema/TreeTransform.h
  test/CodeGen/fenv-access-pragma.c
  test/CodeGen/fenv-math-builtins.c
  test/Parser/pragma-fenv-access.c

Index: test/Parser/pragma-fenv-access.c
===================================================================
--- test/Parser/pragma-fenv-access.c
+++ test/Parser/pragma-fenv-access.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+void f1(void) {
+  int x = 0;
+/* expected-error@+1 {{'#pragma fenv_access' can only appear at file scope or at the start of a compound statement}} */
+#pragma STDC FENV_ACCESS ON
+}
+
+void f2(void) {
+  #pragma STDC FENV_ACCESS OFF
+  #pragma STDC FENV_ACCESS ON 
+}
Index: test/CodeGen/fenv-math-builtins.c
===================================================================
--- test/CodeGen/fenv-math-builtins.c
+++ test/CodeGen/fenv-math-builtins.c
@@ -0,0 +1,84 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -w -S -o - -emit-llvm              %s | FileCheck %s 
+
+// Test codegen of math builtins when using FENV_ACCESS ON.
+// Derived from math-builtins.c and keeps calls in the same order.
+#pragma STDC FENV_ACCESS ON
+
+void foo(double *d, float f, float *fp, long double *l, int *i, const char *c) {
+  __builtin_pow(f,f);        __builtin_powf(f,f);       __builtin_powl(f,f);
+
+// CHECK: declare double @llvm.experimental.constrained.pow.f64(double, double, metadata, metadata) [[MATH_INTRINSIC:#[0-9]+]]
+// CHECK: declare float @llvm.experimental.constrained.pow.f32(float, float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.pow.f80(x86_fp80, x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+  __builtin_powi(f,f);        __builtin_powif(f,f);       __builtin_powil(f,f);
+
+// CHECK: declare double @llvm.experimental.constrained.powi.f64(double, i32, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.powi.f32(float, i32, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.powi.f80(x86_fp80, i32, metadata, metadata) [[MATH_INTRINSIC]]
+
+  /* math */
+  __builtin_cos(f);        __builtin_cosf(f);       __builtin_cosl(f); 
+// CHECK: declare double @llvm.experimental.constrained.cos.f64(double, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.cos.f32(float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.cos.f80(x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+  __builtin_exp(f);        __builtin_expf(f);       __builtin_expl(f);
+// CHECK: declare double @llvm.experimental.constrained.exp.f64(double, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.exp.f32(float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.exp.f80(x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+  __builtin_exp2(f);       __builtin_exp2f(f);      __builtin_exp2l(f); 
+
+// CHECK: declare double @llvm.experimental.constrained.exp2.f64(double, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.exp2.f32(float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.exp2.f80(x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+  __builtin_fma(f,f,f);        __builtin_fmaf(f,f,f);       __builtin_fmal(f,f,f);
+
+// CHECK: declare double @llvm.experimental.constrained.fma.f64(double, double, double, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.fma.f32(float, float, float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.fma.f80(x86_fp80, x86_fp80, x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+  __builtin_log(f);        __builtin_logf(f);       __builtin_logl(f);
+
+// CHECK: declare double @llvm.experimental.constrained.log.f64(double, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.log.f32(float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.log.f80(x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+  __builtin_log10(f);      __builtin_log10f(f);     __builtin_log10l(f);
+
+// CHECK: declare double @llvm.experimental.constrained.log10.f64(double, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.log10.f32(float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.log10.f80(x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+  __builtin_nearbyint(f);  __builtin_nearbyintf(f); __builtin_nearbyintl(f);
+
+// CHECK: declare double @llvm.experimental.constrained.nearbyint.f64(double, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.nearbyint.f32(float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.nearbyint.f80(x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+  __builtin_nextafter(f,f);  __builtin_nextafterf(f,f); __builtin_nextafterl(f,f);
+
+  __builtin_rint(f);       __builtin_rintf(f);      __builtin_rintl(f);
+
+// CHECK: declare double @llvm.experimental.constrained.rint.f64(double, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.rint.f32(float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.rint.f80(x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+  __builtin_sin(f);        __builtin_sinf(f);       __builtin_sinl(f);
+
+// CHECK: declare double @llvm.experimental.constrained.sin.f64(double, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.sin.f32(float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.sin.f80(x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+  __builtin_sqrt(f);       __builtin_sqrtf(f);      __builtin_sqrtl(f); 
+
+// CHECK: declare double @llvm.experimental.constrained.sqrt.f64(double, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare float @llvm.experimental.constrained.sqrt.f32(float, metadata, metadata) [[MATH_INTRINSIC]]
+// CHECK: declare x86_fp80 @llvm.experimental.constrained.sqrt.f80(x86_fp80, metadata, metadata) [[MATH_INTRINSIC]]
+
+}
+
+// CHECK: attributes [[MATH_INTRINSIC]] = { {{.*}}inaccessiblememonly nounwind{{.*}} }
+
Index: test/CodeGen/fenv-access-pragma.c
===================================================================
--- test/CodeGen/fenv-access-pragma.c
+++ test/CodeGen/fenv-access-pragma.c
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -O3 -triple %itanium_abi_triple -emit-llvm -o - %s | FileCheck %s
+
+// Is FENV_ACCESS honored in a simple case?
+float fenv_access_1(float a) {
+// CHECK: _Z13fenv_access_1f
+// CHECK: call double @llvm.experimental.constrained.cos.f64
+#pragma STDC FENV_ACCESS ON
+  return __builtin_cos(a);
+}
+
+// Is FENV_ACCESS state cleared on exiting compound statements?
+float fenv_access_2(float a) {
+  // CHECK: _Z13fenv_access_2f
+  // CHECK: call double @llvm.cos.f64
+  {
+#pragma STDC FENV_ACCESS ON
+  }
+  return __builtin_cos(a);
+}
+
+// Does FENV_ACCESS survive template instantiation?
+template <typename T>
+T template_bcos(T a) {
+#pragma STDC FENV_ACCESS ON
+  return __builtin_cos(a);
+}
+
+float fenv_access_3(float a) {
+  // CHECK: _Z13fenv_access_3f
+  // FIXME: This should be call double @llvm.experimental.constrained.cos.f64
+  // FIXME: Templates are known incomplete with FP_CONTRACT and FENV_ACCESS.
+  // CHECK: call double @llvm.cos.f64
+  return template_bcos<float>(a);
+}
+
+template <typename T>
+class fenv_access_4 {
+  float method(float a) {
+#pragma STDC FENV_ACCESS ON
+    return __builtin_cos(a);
+  }
+};
+
+template class fenv_access_4<int>;
+// CHECK: _ZN13fenv_access_4IiE6methodEf
+// FIXME: This should be call double @llvm.experimental.constrained.cos.f64
+// CHECK: call double @llvm.cos.f64
+
+// Check file-scoped FENV_ACCESS
+#pragma STDC FENV_ACCESS ON
+float fenv_access_5(float a) {
+  // CHECK: _Z13fenv_access_5f
+  // CHECK: call double @llvm.experimental.constrained.cos.f64
+  return __builtin_cos(a);
+}
+
+#pragma STDC FENV_ACCESS OFF
+float fenv_access_6(float a) {
+  // CHECK: _Z13fenv_access_6f
+  // CHECK: call double @llvm.cos.f64
+  return __builtin_cos(a);
+}
+
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h
+++ lib/Sema/TreeTransform.h
@@ -3134,7 +3134,8 @@
     // Build the CallExpr
     ExprResult TheCall = new (SemaRef.Context) CallExpr(
         SemaRef.Context, Callee, SubExprs, Builtin->getCallResultType(),
-        Expr::getValueKindForType(Builtin->getReturnType()), RParenLoc);
+        Expr::getValueKindForType(Builtin->getReturnType()), RParenLoc,
+        SemaRef.FPFeatures);
 
     // Type-check the __builtin_shufflevector expression.
     return SemaRef.SemaBuiltinShuffleVector(cast<CallExpr>(TheCall.get()));
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -6963,7 +6963,7 @@
   // allocator).
   QualType CallResultType = ConversionType.getNonLValueExprType(Context);
   CallExpr Call(Context, &ConversionFn, None, CallResultType, VK,
-                From->getBeginLoc());
+                From->getBeginLoc(), FPFeatures);
   ImplicitConversionSequence ICS =
     TryCopyInitialization(*this, &Call, ToType,
                           /*SuppressUserConversions=*/true,
@@ -11945,7 +11945,8 @@
       // to instantiation time to be able to search into type dependent base
       // classes.
       CallExpr *CE = new (Context) CallExpr(
-          Context, Fn, Args, Context.DependentTy, VK_RValue, RParenLoc);
+          Context, Fn, Args, Context.DependentTy, VK_RValue, RParenLoc,
+          FPFeatures);
       CE->setTypeDependent(true);
       CE->setValueDependent(true);
       CE->setInstantiationDependent(true);
@@ -12827,7 +12828,8 @@
 
   if (isa<CXXPseudoDestructorExpr>(NakedMemExpr))
     return new (Context)
-        CallExpr(Context, MemExprE, Args, Context.VoidTy, VK_RValue, RParenLoc);
+        CallExpr(Context, MemExprE, Args, Context.VoidTy, VK_RValue, RParenLoc,
+                 FPFeatures);
 
   UnbridgedCastsSet UnbridgedCasts;
   if (checkArgPlaceholdersForOverload(*this, Args, UnbridgedCasts))
Index: lib/Sema/SemaOpenMP.cpp
===================================================================
--- lib/Sema/SemaOpenMP.cpp
+++ lib/Sema/SemaOpenMP.cpp
@@ -10781,7 +10781,8 @@
           S.DefaultLvalueConversion(DeclareReductionRef.get()).get());
       Expr *Args[] = {LHS.get(), RHS.get()};
       ReductionOp = new (Context)
-          CallExpr(Context, OVE, Args, Context.VoidTy, VK_RValue, ELoc);
+          CallExpr(Context, OVE, Args, Context.VoidTy, VK_RValue, ELoc, 
+                   S.FPFeatures);
     } else {
       ReductionOp = S.BuildBinOp(
           Stack->getCurScope(), ReductionId.getBeginLoc(), BOK, LHSDRE, RHSDRE);
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -5320,7 +5320,8 @@
       }
 
       return new (Context)
-          CallExpr(Context, Fn, None, Context.VoidTy, VK_RValue, RParenLoc);
+          CallExpr(Context, Fn, None, Context.VoidTy, VK_RValue, RParenLoc,
+                   FPFeatures);
     }
     if (Fn->getType() == Context.PseudoObjectTy) {
       ExprResult result = CheckPlaceholderExpr(Fn);
@@ -5348,7 +5349,8 @@
             Fn->getBeginLoc());
 
         return new (Context) CallExpr(
-            Context, Fn, ArgExprs, Context.DependentTy, VK_RValue, RParenLoc);
+            Context, Fn, ArgExprs, Context.DependentTy, VK_RValue, RParenLoc,
+            FPFeatures);
       }
     }
 
@@ -5377,7 +5379,8 @@
     if (!find.HasFormOfMemberPointer) {
       if (Expr::hasAnyTypeDependentArguments(ArgExprs))
         return new (Context) CallExpr(
-            Context, Fn, ArgExprs, Context.DependentTy, VK_RValue, RParenLoc);
+            Context, Fn, ArgExprs, Context.DependentTy, VK_RValue, RParenLoc,
+            FPFeatures);
       OverloadExpr *ovl = find.Expression;
       if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(ovl))
         return BuildOverloadedCallExpr(
@@ -5530,7 +5533,7 @@
                                                RParenLoc);
   else
     TheCall = new (Context) CallExpr(Context, Fn, Args, Context.BoolTy,
-                                     VK_RValue, RParenLoc);
+                                     VK_RValue, RParenLoc, FPFeatures);
 
   if (!getLangOpts().CPlusPlus) {
     // C cannot always handle TypoExpr nodes in builtin calls and direct
@@ -16424,7 +16427,7 @@
         E = ImpCastExprToType(E, Context.getPointerType(FD->getType()),
                               CK_BuiltinFnToFnPtr).get();
         return new (Context) CallExpr(Context, E, None, Context.IntTy,
-                                      VK_RValue, SourceLocation());
+                                      VK_RValue, SourceLocation(), FPFeatures);
       }
     }
 
Index: lib/Frontend/Rewrite/RewriteObjC.cpp
===================================================================
--- lib/Frontend/Rewrite/RewriteObjC.cpp
+++ lib/Frontend/Rewrite/RewriteObjC.cpp
@@ -2022,7 +2022,7 @@
 
   CallExpr *Exp = new (Context) CallExpr(*Context, ICE, Args,
                                          FT->getCallResultType(*Context),
-                                         VK_RValue, EndLoc);
+                                         VK_RValue, EndLoc, FPOptions());
   return Exp;
 }
 
@@ -2609,7 +2609,8 @@
 
   const FunctionType *FT = msgSendType->getAs<FunctionType>();
   CallExpr *STCE = new (Context) CallExpr(
-      *Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, SourceLocation());
+      *Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, SourceLocation(),
+      FPOptions());
   return STCE;
 }
 
@@ -2702,7 +2703,7 @@
                                                    SourceLocation());
       SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs,
                                         superType, VK_LValue,
-                                        SourceLocation());
+                                        SourceLocation(), FPOptions());
       // The code for super is a little tricky to prevent collision with
       // the structure definition in the header. The rewriter has it's own
       // internal definition (__rw_objc_super) that is uses. This is why
@@ -2796,7 +2797,8 @@
                                                    false, superType, VK_LValue,
                                                    SourceLocation());
       SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs,
-                                        superType, VK_LValue, SourceLocation());
+                                        superType, VK_LValue, SourceLocation(),
+                                        FPOptions());
       // The code for super is a little tricky to prevent collision with
       // the structure definition in the header. The rewriter has it's own
       // internal definition (__rw_objc_super) that is uses. This is why
@@ -2961,7 +2963,8 @@
 
   const FunctionType *FT = msgSendType->getAs<FunctionType>();
   CallExpr *CE = new (Context)
-      CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc);
+      CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc,
+               FPOptions());
   Stmt *ReplacingStmt = CE;
   if (MsgSendStretFlavor) {
     // We have the method which returns a struct/union. Must also generate
@@ -3813,7 +3816,7 @@
   }
   CallExpr *CE = new (Context) CallExpr(*Context, PE, BlkExprs,
                                         Exp->getType(), VK_RValue,
-                                        SourceLocation());
+                                        SourceLocation(), FPOptions());
   return CE;
 }
 
@@ -4528,7 +4531,8 @@
     InitExprs.push_back(FlagExp);
   }
   NewRep = new (Context) CallExpr(*Context, DRE, InitExprs,
-                                  FType, VK_LValue, SourceLocation());
+                                  FType, VK_LValue, SourceLocation(),
+                                  FPOptions());
   NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf,
                              Context->getPointerType(NewRep->getType()),
                              VK_RValue, OK_Ordinary, SourceLocation(), false);
Index: lib/Frontend/Rewrite/RewriteModernObjC.cpp
===================================================================
--- lib/Frontend/Rewrite/RewriteModernObjC.cpp
+++ lib/Frontend/Rewrite/RewriteModernObjC.cpp
@@ -2109,7 +2109,7 @@
 
   CallExpr *Exp =  new (Context) CallExpr(*Context, ICE, Args,
                                           FT->getCallResultType(*Context),
-                                          VK_RValue, EndLoc);
+                                          VK_RValue, EndLoc, FPOptions());
   return Exp;
 }
 
@@ -2692,7 +2692,8 @@
 
   const FunctionType *FT = msgSendType->getAs<FunctionType>();
   CallExpr *CE = new (Context)
-      CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc);
+      CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc,
+               FPOptions());
   ReplaceStmt(Exp, CE);
   return CE;
 }
@@ -2733,7 +2734,8 @@
     InitExprs.push_back(Exp->getElement(i));
   Expr *NSArrayCallExpr =
     new (Context) CallExpr(*Context, NSArrayDRE, InitExprs,
-                           NSArrayFType, VK_LValue, SourceLocation());
+                           NSArrayFType, VK_LValue, SourceLocation(),
+                           FPOptions());
 
   FieldDecl *ARRFD = FieldDecl::Create(*Context, nullptr, SourceLocation(),
                                     SourceLocation(),
@@ -2815,7 +2817,8 @@
 
   const FunctionType *FT = msgSendType->getAs<FunctionType>();
   CallExpr *CE = new (Context)
-      CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc);
+      CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc,
+               FPOptions());
   ReplaceStmt(Exp, CE);
   return CE;
 }
@@ -2864,7 +2867,8 @@
   // (const id [])objects
   Expr *NSValueCallExpr =
     new (Context) CallExpr(*Context, NSDictDRE, ValueExprs,
-                           NSDictFType, VK_LValue, SourceLocation());
+                           NSDictFType, VK_LValue, SourceLocation(), 
+                           FPOptions());
 
   FieldDecl *ARRFD = FieldDecl::Create(*Context, nullptr, SourceLocation(),
                                        SourceLocation(),
@@ -2884,7 +2888,8 @@
   // (const id <NSCopying> [])keys
   Expr *NSKeyCallExpr =
     new (Context) CallExpr(*Context, NSDictDRE, KeyExprs,
-                           NSDictFType, VK_LValue, SourceLocation());
+                           NSDictFType, VK_LValue, SourceLocation(),
+                           FPOptions());
 
   MemberExpr *DictLiteralKeyME = new (Context)
       MemberExpr(NSKeyCallExpr, false, SourceLocation(), ARRFD,
@@ -2969,7 +2974,8 @@
 
   const FunctionType *FT = msgSendType->getAs<FunctionType>();
   CallExpr *CE = new (Context)
-      CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc);
+      CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc,
+               FPOptions());
   ReplaceStmt(Exp, CE);
   return CE;
 }
@@ -3182,7 +3188,8 @@
   DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, castType, VK_RValue,
                                                SourceLocation());
   CallExpr *STCE = new (Context) CallExpr(*Context, DRE, MsgExprs,
-                                          castType, VK_LValue, SourceLocation());
+                                          castType, VK_LValue, 
+                                          SourceLocation(), FPOptions());
 
   FieldDecl *FieldD = FieldDecl::Create(*Context, nullptr, SourceLocation(),
                                     SourceLocation(),
@@ -3284,7 +3291,7 @@
                                                    SourceLocation());
       SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs,
                                         superType, VK_LValue,
-                                        SourceLocation());
+                                        SourceLocation(), FPOptions());
       // The code for super is a little tricky to prevent collision with
       // the structure definition in the header. The rewriter has it's own
       // internal definition (__rw_objc_super) that is uses. This is why
@@ -3378,7 +3385,8 @@
                                                    false, superType, VK_LValue,
                                                    SourceLocation());
       SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs,
-                                        superType, VK_LValue, SourceLocation());
+                                        superType, VK_LValue, SourceLocation(),
+                                        FPOptions());
       // The code for super is a little tricky to prevent collision with
       // the structure definition in the header. The rewriter has it's own
       // internal definition (__rw_objc_super) that is uses. This is why
@@ -3543,7 +3551,8 @@
 
   const FunctionType *FT = msgSendType->getAs<FunctionType>();
   CallExpr *CE = new (Context)
-      CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc);
+      CallExpr(*Context, PE, MsgExprs, FT->getReturnType(), VK_RValue, EndLoc,
+               FPOptions());
   Stmt *ReplacingStmt = CE;
   if (MsgSendStretFlavor) {
     // We have the method which returns a struct/union. Must also generate
@@ -4657,7 +4666,7 @@
   }
   CallExpr *CE = new (Context) CallExpr(*Context, PE, BlkExprs,
                                         Exp->getType(), VK_RValue,
-                                        SourceLocation());
+                                        SourceLocation(), FPOptions());
   return CE;
 }
 
@@ -5405,7 +5414,8 @@
     InitExprs.push_back(FlagExp);
   }
   NewRep = new (Context) CallExpr(*Context, DRE, InitExprs,
-                                  FType, VK_LValue, SourceLocation());
+                                  FType, VK_LValue, SourceLocation(), 
+                                  FPOptions());
 
   if (GlobalBlockExpr) {
     assert (!GlobalConstructionExp &&
Index: lib/CodeGen/CGBuiltin.cpp
===================================================================
--- lib/CodeGen/CGBuiltin.cpp
+++ lib/CodeGen/CGBuiltin.cpp
@@ -29,6 +29,7 @@
 #include "llvm/IR/CallSite.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/MDBuilder.h"
 #include "llvm/Support/ConvertUTF.h"
@@ -230,8 +231,74 @@
                        ValueType);
 }
 
+static void getConstrainedMetadata(CodeGenFunction &CGF,
+                                   llvm::Value **RoundingV,
+                                   llvm::Value **ExceptV)
+{
+  if (RoundingV) {
+    auto *RoundingMD = MDString::get(CGF.getLLVMContext(),
+                                          "round.dynamic");
+    *RoundingV = MetadataAsValue::get(CGF.getLLVMContext(), RoundingMD);
+  }
+
+  if (ExceptV) {
+    auto *ExceptMD = MDString::get(CGF.getLLVMContext(),
+                                          "fpexcept.strict");
+    *ExceptV = MetadataAsValue::get(CGF.getLLVMContext(), ExceptMD);
+  }
+}
+
 // Emit a simple mangled intrinsic that has 1 argument and a return type
 // matching the argument type.
+// Include metadata needed since this is for a constrained FP intrinsic.
+static Value *emitUnaryConstrainedFPBuiltin(CodeGenFunction &CGF,
+                               const CallExpr *E,
+                               unsigned IntrinsicID) {
+  llvm::Value *Src0 = CGF.EmitScalarExpr(E->getArg(0));
+  llvm::Value *Rounding;
+  llvm::Value *Except;
+
+  getConstrainedMetadata(CGF, &Rounding, &Except);
+
+  Value *F = CGF.CGM.getIntrinsic(IntrinsicID, Src0->getType());
+  return CGF.Builder.CreateCall(F, { Src0, Rounding, Except });
+}  
+
+// Emit an intrinsic that has 2 operands of the same type as its result.
+// Include metadata needed since this is for a constrained FP intrinsic.
+static Value *emitBinaryConstrainedFPBuiltin(CodeGenFunction &CGF,
+                                const CallExpr *E,
+                                unsigned IntrinsicID) {
+  llvm::Value *Src0 = CGF.EmitScalarExpr(E->getArg(0));
+  llvm::Value *Src1 = CGF.EmitScalarExpr(E->getArg(1));
+  llvm::Value *Rounding;
+  llvm::Value *Except;
+
+  getConstrainedMetadata(CGF, &Rounding, &Except);
+
+  Value *F = CGF.CGM.getIntrinsic(IntrinsicID, Src0->getType());
+  return CGF.Builder.CreateCall(F, { Src0, Src1, Rounding, Except });
+}
+
+// Emit an intrinsic that has 3 operands of the same type as its result.
+// Include metadata needed since this is for a constrained FP intrinsic.
+static Value *emitTernaryConstrainedFPBuiltin(CodeGenFunction &CGF,
+                                 const CallExpr *E,
+                                 unsigned IntrinsicID) {
+  llvm::Value *Src0 = CGF.EmitScalarExpr(E->getArg(0));
+  llvm::Value *Src1 = CGF.EmitScalarExpr(E->getArg(1));
+  llvm::Value *Src2 = CGF.EmitScalarExpr(E->getArg(2));
+  llvm::Value *Rounding;
+  llvm::Value *Except;
+
+  getConstrainedMetadata(CGF, &Rounding, &Except);
+
+  Value *F = CGF.CGM.getIntrinsic(IntrinsicID, Src0->getType());
+  return CGF.Builder.CreateCall(F, { Src0, Src1, Src2, Rounding, Except });
+}
+
+// Emit a simple mangled intrinsic that has 1 argument and a return type
+// matching the argument type.
 static Value *emitUnaryBuiltin(CodeGenFunction &CGF,
                                const CallExpr *E,
                                unsigned IntrinsicID) {
@@ -1312,23 +1379,35 @@
     case Builtin::BI__builtin_cos:
     case Builtin::BI__builtin_cosf:
     case Builtin::BI__builtin_cosl:
-      return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::cos));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitUnaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_cos));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::cos));
 
     case Builtin::BIexp:
     case Builtin::BIexpf:
     case Builtin::BIexpl:
     case Builtin::BI__builtin_exp:
     case Builtin::BI__builtin_expf:
     case Builtin::BI__builtin_expl:
-      return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::exp));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitUnaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_exp));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::exp));
 
     case Builtin::BIexp2:
     case Builtin::BIexp2f:
     case Builtin::BIexp2l:
     case Builtin::BI__builtin_exp2:
     case Builtin::BI__builtin_exp2f:
     case Builtin::BI__builtin_exp2l:
-      return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::exp2));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitUnaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_exp2));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::exp2));
 
     case Builtin::BIfabs:
     case Builtin::BIfabsf:
@@ -1353,7 +1432,11 @@
     case Builtin::BI__builtin_fma:
     case Builtin::BI__builtin_fmaf:
     case Builtin::BI__builtin_fmal:
-      return RValue::get(emitTernaryBuiltin(*this, E, Intrinsic::fma));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitTernaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_fma));
+      else
+        return RValue::get(emitTernaryBuiltin(*this, E, Intrinsic::fma));
 
     case Builtin::BIfmax:
     case Builtin::BIfmaxf:
@@ -1390,47 +1473,71 @@
     case Builtin::BI__builtin_log:
     case Builtin::BI__builtin_logf:
     case Builtin::BI__builtin_logl:
-      return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::log));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitUnaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_log));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::log));
 
     case Builtin::BIlog10:
     case Builtin::BIlog10f:
     case Builtin::BIlog10l:
     case Builtin::BI__builtin_log10:
     case Builtin::BI__builtin_log10f:
     case Builtin::BI__builtin_log10l:
-      return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::log10));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitUnaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_log10));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::log10));
 
     case Builtin::BIlog2:
     case Builtin::BIlog2f:
     case Builtin::BIlog2l:
     case Builtin::BI__builtin_log2:
     case Builtin::BI__builtin_log2f:
     case Builtin::BI__builtin_log2l:
-      return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::log2));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitUnaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_log2));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::log2));
 
     case Builtin::BInearbyint:
     case Builtin::BInearbyintf:
     case Builtin::BInearbyintl:
     case Builtin::BI__builtin_nearbyint:
     case Builtin::BI__builtin_nearbyintf:
     case Builtin::BI__builtin_nearbyintl:
-      return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::nearbyint));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitUnaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_nearbyint));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::nearbyint));
 
     case Builtin::BIpow:
     case Builtin::BIpowf:
     case Builtin::BIpowl:
     case Builtin::BI__builtin_pow:
     case Builtin::BI__builtin_powf:
     case Builtin::BI__builtin_powl:
-      return RValue::get(emitBinaryBuiltin(*this, E, Intrinsic::pow));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitBinaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_pow));
+      else
+        return RValue::get(emitBinaryBuiltin(*this, E, Intrinsic::pow));
 
     case Builtin::BIrint:
     case Builtin::BIrintf:
     case Builtin::BIrintl:
     case Builtin::BI__builtin_rint:
     case Builtin::BI__builtin_rintf:
     case Builtin::BI__builtin_rintl:
-      return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::rint));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitUnaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_rint));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::rint));
 
     case Builtin::BIround:
     case Builtin::BIroundf:
@@ -1446,15 +1553,23 @@
     case Builtin::BI__builtin_sin:
     case Builtin::BI__builtin_sinf:
     case Builtin::BI__builtin_sinl:
-      return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::sin));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitUnaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_sin));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::sin));
 
     case Builtin::BIsqrt:
     case Builtin::BIsqrtf:
     case Builtin::BIsqrtl:
     case Builtin::BI__builtin_sqrt:
     case Builtin::BI__builtin_sqrtf:
     case Builtin::BI__builtin_sqrtl:
-      return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::sqrt));
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitUnaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_sqrt));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::sqrt));
 
     case Builtin::BItrunc:
     case Builtin::BItruncf:
@@ -1814,13 +1929,12 @@
 
   case Builtin::BI__builtin_powi:
   case Builtin::BI__builtin_powif:
-  case Builtin::BI__builtin_powil: {
-    Value *Base = EmitScalarExpr(E->getArg(0));
-    Value *Exponent = EmitScalarExpr(E->getArg(1));
-    llvm::Type *ArgType = Base->getType();
-    Value *F = CGM.getIntrinsic(Intrinsic::powi, ArgType);
-    return RValue::get(Builder.CreateCall(F, {Base, Exponent}));
-  }
+  case Builtin::BI__builtin_powil:
+      if (E->isFEnvAccessOn())
+        return RValue::get(emitBinaryConstrainedFPBuiltin(*this, E, 
+                           Intrinsic::experimental_constrained_powi));
+      else
+        return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::powi));
 
   case Builtin::BI__builtin_isgreater:
   case Builtin::BI__builtin_isgreaterequal:
Index: lib/Analysis/BodyFarm.cpp
===================================================================
--- lib/Analysis/BodyFarm.cpp
+++ lib/Analysis/BodyFarm.cpp
@@ -271,7 +271,8 @@
   }
 
   return new (C)
-      CallExpr(C, SubExpr, CallArgs, C.VoidTy, VK_RValue, SourceLocation());
+      CallExpr(C, SubExpr, CallArgs, C.VoidTy, VK_RValue, SourceLocation(),
+               FPOptions());
 }
 
 static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M,
@@ -516,7 +517,8 @@
       /*args=*/None,
       /*QualType=*/C.VoidTy,
       /*ExprValueType=*/VK_RValue,
-      /*SourceLocation=*/SourceLocation());
+      /*SourceLocation=*/SourceLocation(),
+      /*FPOptions=*/FPOptions());
 
   // (2) Create the assignment to the predicate.
   Expr *DoneValue =
@@ -581,7 +583,7 @@
   DeclRefExpr *DR = M.makeDeclRefExpr(PV);
   ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty);
   CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue,
-                                  SourceLocation());
+                                  SourceLocation(), FPOptions());
   return CE;
 }
 
Index: lib/AST/Expr.cpp
===================================================================
--- lib/AST/Expr.cpp
+++ lib/AST/Expr.cpp
@@ -1194,11 +1194,12 @@
 
 CallExpr::CallExpr(const ASTContext &C, StmtClass SC, Expr *fn,
                    ArrayRef<Expr *> preargs, ArrayRef<Expr *> args, QualType t,
-                   ExprValueKind VK, SourceLocation rparenloc)
+                   ExprValueKind VK, SourceLocation rparenloc,
+                   FPOptions FPFeatures)
     : Expr(SC, t, VK, OK_Ordinary, fn->isTypeDependent(),
            fn->isValueDependent(), fn->isInstantiationDependent(),
            fn->containsUnexpandedParameterPack()),
-      NumArgs(args.size()) {
+      NumArgs(args.size()), FPFeatures(FPFeatures.getInt()) {
 
   unsigned NumPreArgs = preargs.size();
   SubExprs = new (C) Stmt *[args.size()+PREARGS_START+NumPreArgs];
@@ -1218,13 +1219,15 @@
 
 CallExpr::CallExpr(const ASTContext &C, StmtClass SC, Expr *fn,
                    ArrayRef<Expr *> args, QualType t, ExprValueKind VK,
-                   SourceLocation rparenloc)
-    : CallExpr(C, SC, fn, ArrayRef<Expr *>(), args, t, VK, rparenloc) {}
+                   SourceLocation rparenloc, FPOptions FPFeatures)
+    : CallExpr(C, SC, fn, ArrayRef<Expr *>(), args, t, VK, rparenloc, 
+               FPFeatures) {}
 
 CallExpr::CallExpr(const ASTContext &C, Expr *fn, ArrayRef<Expr *> args,
-                   QualType t, ExprValueKind VK, SourceLocation rparenloc)
-    : CallExpr(C, CallExprClass, fn, ArrayRef<Expr *>(), args, t, VK, rparenloc) {
-}
+                   QualType t, ExprValueKind VK, SourceLocation rparenloc,
+                   FPOptions FPFeatures)
+    : CallExpr(C, CallExprClass, fn, ArrayRef<Expr *>(), args, t, VK, 
+               rparenloc, FPFeatures) {}
 
 CallExpr::CallExpr(const ASTContext &C, StmtClass SC, EmptyShell Empty)
     : CallExpr(C, SC, /*NumPreArgs=*/0, Empty) {}
Index: lib/AST/ASTImporter.cpp
===================================================================
--- lib/AST/ASTImporter.cpp
+++ lib/AST/ASTImporter.cpp
@@ -6716,7 +6716,7 @@
   return new (Importer.getToContext())
     CallExpr(Importer.getToContext(), ToCallee,
              llvm::makeArrayRef(ToArgs_Copied, NumArgs), T, E->getValueKind(),
-             Importer.Import(E->getRParenLoc()));
+             Importer.Import(E->getRParenLoc()), E->getFPFeatures());
 }
 
 Optional<LambdaCapture>
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -81,9 +81,6 @@
 
   SourceRange Range;
 
-  // Only meaningful for floating point types.
-  FPOptions FPFeatures;
-
   SourceRange getSourceRangeImpl() const LLVM_READONLY;
 
 public:
@@ -93,8 +90,9 @@
   CXXOperatorCallExpr(ASTContext& C, OverloadedOperatorKind Op, Expr *fn,
                       ArrayRef<Expr*> args, QualType t, ExprValueKind VK,
                       SourceLocation operatorloc, FPOptions FPFeatures)
-      : CallExpr(C, CXXOperatorCallExprClass, fn, args, t, VK, operatorloc),
-        Operator(Op), FPFeatures(FPFeatures) {
+      : CallExpr(C, CXXOperatorCallExprClass, fn, args, t, VK, operatorloc, 
+                 FPFeatures),
+        Operator(Op) {
     Range = getSourceRangeImpl();
   }
 
@@ -148,16 +146,10 @@
     return T->getStmtClass() == CXXOperatorCallExprClass;
   }
 
-  // Set the FP contractability status of this operator. Only meaningful for
-  // operations on floating point types.
-  void setFPFeatures(FPOptions F) { FPFeatures = F; }
-
-  FPOptions getFPFeatures() const { return FPFeatures; }
-
   // Get the FP contractability status of this operator. Only meaningful for
   // operations on floating point types.
   bool isFPContractableWithinStatement() const {
-    return FPFeatures.allowFPContractWithinStatement();
+    return getFPFeatures().allowFPContractWithinStatement();
   }
 };
 
@@ -173,7 +165,8 @@
 public:
   CXXMemberCallExpr(ASTContext &C, Expr *fn, ArrayRef<Expr*> args,
                     QualType t, ExprValueKind VK, SourceLocation RP)
-      : CallExpr(C, CXXMemberCallExprClass, fn, args, t, VK, RP) {}
+      : CallExpr(C, CXXMemberCallExprClass, fn, args, t, VK, RP, FPOptions(0)) 
+        {}
 
   CXXMemberCallExpr(ASTContext &C, EmptyShell Empty)
       : CallExpr(C, CXXMemberCallExprClass, Empty) {}
@@ -216,7 +209,8 @@
   CUDAKernelCallExpr(ASTContext &C, Expr *fn, CallExpr *Config,
                      ArrayRef<Expr*> args, QualType t, ExprValueKind VK,
                      SourceLocation RP)
-      : CallExpr(C, CUDAKernelCallExprClass, fn, Config, args, t, VK, RP) {}
+      : CallExpr(C, CUDAKernelCallExprClass, fn, Config, args, t, VK, RP, 
+                 FPOptions(0)) {}
 
   CUDAKernelCallExpr(ASTContext &C, EmptyShell Empty)
       : CallExpr(C, CUDAKernelCallExprClass, END_PREARG, Empty) {}
@@ -501,7 +495,8 @@
   UserDefinedLiteral(const ASTContext &C, Expr *Fn, ArrayRef<Expr*> Args,
                      QualType T, ExprValueKind VK, SourceLocation LitEndLoc,
                      SourceLocation SuffixLoc)
-      : CallExpr(C, UserDefinedLiteralClass, Fn, Args, T, VK, LitEndLoc),
+      : CallExpr(C, UserDefinedLiteralClass, Fn, Args, T, VK, LitEndLoc,
+                 FPOptions(0)),
         UDSuffixLoc(SuffixLoc) {}
 
   explicit UserDefinedLiteral(const ASTContext &C, EmptyShell Empty)
Index: include/clang/AST/Expr.h
===================================================================
--- include/clang/AST/Expr.h
+++ include/clang/AST/Expr.h
@@ -2409,15 +2409,20 @@
   unsigned NumArgs;
   SourceLocation RParenLoc;
 
+  // This is only meaningful for operations on floating point types and 0
+  // otherwise.
+  unsigned FPFeatures : 3;
+
   void updateDependenciesFromArg(Expr *Arg);
 
 protected:
   // These versions of the constructor are for derived classes.
   CallExpr(const ASTContext &C, StmtClass SC, Expr *fn,
            ArrayRef<Expr *> preargs, ArrayRef<Expr *> args, QualType t,
-           ExprValueKind VK, SourceLocation rparenloc);
+           ExprValueKind VK, SourceLocation rparenloc, FPOptions FPFeatures);
   CallExpr(const ASTContext &C, StmtClass SC, Expr *fn, ArrayRef<Expr *> args,
-           QualType t, ExprValueKind VK, SourceLocation rparenloc);
+           QualType t, ExprValueKind VK, SourceLocation rparenloc, 
+           FPOptions FPFeatures);
   CallExpr(const ASTContext &C, StmtClass SC, unsigned NumPreArgs,
            EmptyShell Empty);
 
@@ -2438,7 +2443,7 @@
 
 public:
   CallExpr(const ASTContext& C, Expr *fn, ArrayRef<Expr*> args, QualType t,
-           ExprValueKind VK, SourceLocation rparenloc);
+           ExprValueKind VK, SourceLocation rparenloc, FPOptions FPFeatures);
 
   /// Build an empty call expression.
   CallExpr(const ASTContext &C, StmtClass SC, EmptyShell Empty);
@@ -2578,6 +2583,18 @@
     return const_child_range(&SubExprs[0], &SubExprs[0] + NumArgs +
                                                getNumPreArgs() + PREARGS_START);
   }
+
+  // Set the FP contractability status of this operator. Only meaningful for
+  // operations on floating point types.
+  void setFPFeatures(FPOptions F) { FPFeatures = F.getInt(); }
+
+  FPOptions getFPFeatures() const { return FPOptions(FPFeatures); }
+
+  // Get the FENV_ACCESS status of this operator. Only meaningful for
+  // operations on floating point types.
+  bool isFEnvAccessOn() const {
+    return FPOptions(FPFeatures).allowFEnvAccess();
+  }
 };
 
 /// Extra data stored in some MemberExpr objects.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D51372: FENV_ACCESS ... Kevin P. Neal via Phabricator via cfe-commits

Reply via email to