wolfgangp updated this revision to Diff 175307.
wolfgangp added a comment.

Rebased on r347577 with some test cases.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D41044/new/

https://reviews.llvm.org/D41044

Files:
  include/clang/Driver/Options.td
  include/clang/Frontend/CodeGenOptions.def
  lib/CodeGen/CGCall.cpp
  lib/CodeGen/CGCleanup.cpp
  lib/CodeGen/CGCleanup.h
  lib/CodeGen/CGDecl.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/CodeGen/CodeGenModule.h
  lib/CodeGen/EHScopeStack.h
  lib/Driver/ToolChains/Clang.cpp
  lib/Frontend/CompilerInvocation.cpp
  test/CodeGen/fake-use-determinism.c
  test/CodeGen/fake-use-landingpad.c
  test/CodeGen/fake-use-noreturn.c
  test/CodeGen/fake-use-return-line.c
  test/CodeGen/fake-use-sanitizer.cpp
  test/CodeGen/fake-use-scalar.c
  test/CodeGen/fake-use-while.c
  test/CodeGen/fake-use.cpp
  test/CodeGen/no-fake-use-O0.cpp

Index: test/CodeGen/no-fake-use-O0.cpp
===================================================================
--- /dev/null
+++ test/CodeGen/no-fake-use-O0.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 %s -O0 -emit-llvm -fextend-this-ptr -o -  | FileCheck %s
+// RUN: %clang_cc1 %s -O0 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
+// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-this-ptr -o -  | FileCheck -check-prefix=OPT %s
+// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-lifetimes -o - | FileCheck -check-prefix=OPT %s
+// RUN: %clang_cc1 %s -Os -emit-llvm -fextend-this-ptr -o -  | FileCheck -check-prefix=OPT %s
+// RUN: %clang_cc1 %s -Os -emit-llvm -fextend-lifetimes -o - | FileCheck -check-prefix=OPT %s
+// RUN: %clang_cc1 %s -Oz -emit-llvm -fextend-this-ptr -o -  | FileCheck -check-prefix=OPT %s
+// RUN: %clang_cc1 %s -Oz -emit-llvm -fextend-lifetimes -o - | FileCheck -check-prefix=OPT %s
+// Check that we do not generate a fake_use call when we are not optimizing. 
+
+extern void bar();
+
+class v
+{
+public:
+    int x;
+    int y;
+    int z;
+    int w;
+
+    v(int a, int b, int c, int d) : x(a), y(b), z(c), w(d) {}
+};
+
+class w
+{
+public:
+    v test(int, int, int, int, int, int, int, int, int, int);
+    w(int in): a(in), b(1234) {}
+
+private:
+    int a;
+    int b;
+};
+
+v w::test(int q, int w, int e, int r, int t, int y, int u, int i, int o, int p)
+{
+// CHECK: define{{.*}}test
+    int res = q*w + e - r*t + y*u*i*o*p;
+    int res2 = (w + e + r + t + y + o)*(p*q);
+    int res3 = res + res2;
+    int res4 = q*e + t*y*i + p*e*w * 6 * 4 * 3;
+
+    v V(res, res2, res3, res4);
+
+    bar();
+// CHECK-NOT: call void (...) @llvm.fake.use
+// OPT:       call void (...) @llvm.fake.use
+    return V;
+}
Index: test/CodeGen/fake-use.cpp
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-this-ptr -o - | FileCheck %s
+// Check that we generate a fake_use call with the 'this' pointer as argument.
+// The call should appear after the call to bar().
+
+extern void bar();
+
+class v
+{
+public:
+    int x;
+    int y;
+    int z;
+    int w;
+
+    v(int a, int b, int c, int d) : x(a), y(b), z(c), w(d) {}
+};
+
+class w
+{
+public:
+    v test(int, int, int, int, int, int, int, int, int, int);
+    w(int in): a(in), b(1234) {}
+
+private:
+    int a;
+    int b;
+};
+
+v w::test(int q, int w, int e, int r, int t, int y, int u, int i, int o, int p)
+{
+// CHECK: define{{.*}}test
+    int res = q*w + e - r*t + y*u*i*o*p;
+    int res2 = (w + e + r + t + y + o)*(p*q);
+    int res3 = res + res2;
+    int res4 = q*e + t*y*i + p*e*w * 6 * 4 * 3;
+
+    v V(res, res2, res3, res4);
+
+    bar();
+// CHECK: call{{.*}}bar
+// CHECK: call void (...) @llvm.fake.use(%class.w* %this)
+    return V;
+// CHECK: ret
+}
Index: test/CodeGen/fake-use-while.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-while.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 %s -S -emit-llvm -fextend-lifetimes -o %t.ll
+//
+// Check we don't assert when there is no more code after a while statement
+// and the body of the while statement ends in a return, i.e. no insertion point
+// is available.
+
+// CHECK: define{{.*}}main
+// CHECK: call{{.*}}llvm.fake.use
+
+void foo() {
+  {
+    while (1) {
+      int ret;
+      if (1)
+        return;
+    }
+  }
+}
Index: test/CodeGen/fake-use-scalar.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-scalar.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 %s -O2 -emit-llvm -fextend-lifetimes -o - | FileCheck %s
+// Make sure we don't generate fake.use for non-scalar 
+// variables.
+
+struct A {
+  unsigned long t;
+  char c[1024];
+  unsigned char r[32];
+};
+
+
+int foo()
+{
+  struct A s;
+  struct A v[128];
+  char c[32];
+  return 0;
+}
+
+// CHECK-NOT:  fake.use
Index: test/CodeGen/fake-use-sanitizer.cpp
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-sanitizer.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-lifetimes -fsanitize=null -fsanitize-trap=null -o - | FileCheck --check-prefix TRAP %s
+// RUN: %clang_cc1 %s -O1 -emit-llvm -fextend-lifetimes -o - | FileCheck --check-prefix FAKEUSE %s
+// REQUIRES: asserts
+
+// With -fextend-lifetimes the compiler generated a fake.use of a
+// reference variable at the end of the function, in the cleanup block. This prompted the 
+// address sanitizer to verify that the variable has been allocated properly, even when 
+// the functions returns early.
+// We check that sanitizers are disabled for code generated for the benefit of fake.use 
+// intrinsics, as well as that the fake.use is no longer generated in the cleanup block.
+// It should be generated in the block preceding the cleanup block instead.
+
+struct A { short s1, s2; };
+extern A& getA();
+
+void foo()
+{
+  auto& va = getA();
+  short s = va.s1 & ~va.s2;
+  if (s == 0)
+    return;
+
+  auto& vb = getA();
+}
+
+// TRAP:         define{{.*}}foo
+// TRAP:         [[COMPARE:%[^\s]*]] = icmp eq
+// TRAP-NOT:     br i1 [[COMPARE]]{{.*}}trap
+// TRAP:         br i1 [[COMPARE]]{{.*}}%if.end
+// TRAP-NOT:     trap:
+// TRAP:         if.end:
+// TRAP-NOT:     call{{.*}}llvm.trap
+
+// FAKEUSE:      if.end:
+// FAKEUSE-NEXT: [[CALL:%[^\s]*]] = {{.*}}call{{.*}}getA
+// FAKEUSE-NOT:  br{{.*}}cleanup
+// FAKEUSE:      call{{.*}}fake.use({{.*}}[[CALL]])
+// FAKEUSE:      cleanup:
Index: test/CodeGen/fake-use-return-line.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-return-line.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -S -emit-llvm -O2 -debug-info-kind=limited -fextend-lifetimes -o - %s | FileCheck %s
+int main()
+{
+  volatile int a = 1;
+  int b = a + 2;
+  return b;
+}
+// CHECK: define{{.*}}@main
+// CHECK: ret i32{{.*}}!dbg ![[MDINDEX:[0-9]*]]
+// CHECK: ![[MDINDEX]] = !DILocation(line: 6
Index: test/CodeGen/fake-use-noreturn.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-noreturn.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 %s -S -emit-llvm -fextend-lifetimes -o %t.ll
+//
+// Check we don't assert when we have a return in a nested conditional and
+// there is no code at the end of the function.
+
+// CHECK: define{{.*}}main
+// CHECK: call{{.*}}llvm.fake.use
+
+void foo(int i) {
+   while (0)
+     if (1)
+       return;
+}
Index: test/CodeGen/fake-use-landingpad.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-landingpad.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 %s -O3 -emit-llvm -fextend-lifetimes -fexceptions -o - | FileCheck %s
+// REQUIRES: asserts
+
+// Check that fake uses do not mistakenly cause a landing pad to be generated when
+// exceptions are enabled.
+
+extern void bar(int);
+void foo(int p) {
+  int a = 17;
+  bar(a);
+}
+
+// CHECK:      define {{.*}} @foo
+// CHECK-NOT:  personality
+// CHECK:      entry:
+// CHECK-NOT:  landingpad
Index: test/CodeGen/fake-use-determinism.c
===================================================================
--- /dev/null
+++ test/CodeGen/fake-use-determinism.c
@@ -0,0 +1,26 @@
+// RUN: %clang -S -O2 -emit-llvm -fextend-lifetimes %s -o - | FileCheck %s
+// REQUIRES: asserts
+// 
+// Non-deterministic order of fake.use instructions.
+//
+// We are checking that the fake.use calls for i, j and k appear
+// in a particular order. It is not the order itself that is important
+// but that it remains the same between different test runs.
+//
+// Different targets may have different forms.  For example, on X86, we get a
+// line like:
+//      tail call void (...) @llvm.fake.use(i32 %i)
+// but on ARM, we get a line like:
+//      tail call arm_aapcs_vfpcc void (...) @llvm.fake.use(i32 %i)
+
+// CHECK:       call {{.*}}void (...) @llvm.fake.use(i32 %k)
+// CHECK-NEXT:  call {{.*}}void (...) @llvm.fake.use(i32 %j)
+// CHECK-NEXT:  call {{.*}}void (...) @llvm.fake.use(i32 %i)
+
+extern void bar();
+void foo(int i, int j, int k)
+{
+   for (int l = 0; l < i; l++) {
+      bar();
+   }
+}
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -1152,6 +1152,11 @@
   Opts.CudaGpuBinaryFileName =
       Args.getLastArgValue(OPT_fcuda_include_gpubinary);
 
+  Opts.ExtendThisPtr = Opts.OptimizationLevel > 0 &&
+                       Args.hasArg(OPT_fextend_this_ptr);
+  Opts.ExtendLifetimes = Opts.OptimizationLevel > 0 &&
+                         Args.hasArg(OPT_fextend_lifetimes);
+
   Opts.Backchain = Args.hasArg(OPT_mbackchain);
 
   Opts.EmitCheckPathComponentsToStrip = getLastArgIntValue(
Index: lib/Driver/ToolChains/Clang.cpp
===================================================================
--- lib/Driver/ToolChains/Clang.cpp
+++ lib/Driver/ToolChains/Clang.cpp
@@ -4970,6 +4970,11 @@
   if (Args.hasArg(options::OPT_fretain_comments_from_system_headers))
     CmdArgs.push_back("-fretain-comments-from-system-headers");
 
+  if (Args.hasArg(options::OPT_fextend_this_ptr))
+    CmdArgs.push_back("-fextend-this-ptr");
+  if (Args.hasArg(options::OPT_fextend_lifetimes))
+    CmdArgs.push_back("-fextend-lifetimes");
+
   // Forward -fcomment-block-commands to -cc1.
   Args.AddAllArgs(CmdArgs, options::OPT_fcomment_block_commands);
   // Forward -fparse-all-comments to -cc1.
Index: lib/CodeGen/EHScopeStack.h
===================================================================
--- lib/CodeGen/EHScopeStack.h
+++ lib/CodeGen/EHScopeStack.h
@@ -93,6 +93,11 @@
 
   LifetimeMarker = 0x8,
   NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup,
+
+  // FakeUse needs to be recognized as a special cleanup similar to lifetime
+  // markers chiefly to be ignored in most contexts.
+  FakeUse = 0x10,
+  NormalFakeUse = FakeUse | NormalCleanup,
 };
 
 /// A stack of scopes which respond to exceptions, including cleanups
Index: lib/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -495,6 +495,9 @@
   /// void @llvm.lifetime.end(i64 %size, i8* nocapture <ptr>)
   llvm::Constant *LifetimeEndFn = nullptr;
 
+  /// void @llvm.fake.use(i8* nocapture <ptr>)
+  llvm::Constant *FakeUseFn = nullptr;
+
   GlobalDecl initializedGlobalDecl;
 
   std::unique_ptr<SanitizerMetadata> SanitizerMD;
@@ -1007,6 +1010,7 @@
 
   llvm::Constant *getLLVMLifetimeStartFn();
   llvm::Constant *getLLVMLifetimeEndFn();
+  llvm::Constant *getLLVMFakeUseFn();
 
   // Make sure that this type is translated.
   void UpdateCompletedType(const TagDecl *TD);
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -505,6 +505,21 @@
     }
   };
 
+  // We are using objects of this 'cleanup' class to emit fake.use calls
+  // for -fextend-lifetimes. They are placed at the end of a variable's 
+  // scope analogous to lifetime markers.
+  class FakeUse final : public EHScopeStack::Cleanup {
+      Address Addr;
+
+  public:
+      FakeUse(Address addr)
+          : Addr(addr) {}
+
+      void Emit(CodeGenFunction &CGF, Flags flags) override {
+          CGF.EmitFakeUse(Addr);
+      }
+  };
+
   /// Header for data within LifetimeExtendedCleanupStack.
   struct LifetimeExtendedCleanupHeader {
     /// The size of the following cleanup object.
@@ -3966,6 +3981,8 @@
 
   RValue EmitAtomicExpr(AtomicExpr *E);
 
+  void EmitFakeUse(Address Addr);
+
   //===--------------------------------------------------------------------===//
   //                         Annotations Emission
   //===--------------------------------------------------------------------===//
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp
+++ lib/CodeGen/CGDecl.cpp
@@ -1089,6 +1089,15 @@
   C->setDoesNotThrow();
 }
 
+void CodeGenFunction::EmitFakeUse(Address Addr) {
+  auto NL = ApplyDebugLocation::CreateEmpty(*this);
+  llvm::Value *V = Builder.CreateLoad(Addr, "fake.use");
+  llvm::CallInst *C =
+      Builder.CreateCall(CGM.getLLVMFakeUseFn(), { V });
+  C->setDoesNotThrow();
+  C->setTailCallKind(llvm::CallInst::TCK_NoTail);
+}
+
 void CodeGenFunction::EmitAndRegisterVariableArrayDimensions(
     CGDebugInfo *DI, const VarDecl &D, bool EmitDebugInfo) {
   // For each dimension stores its QualType and corresponding
@@ -1350,6 +1359,12 @@
     EHStack.pushCleanup<CallLifetimeEnd>(NormalEHLifetimeMarker,
                                          emission.getOriginalAllocatedAddress(),
                                          emission.getSizeForLifetimeMarkers());
+  // Analogous to lifetime markers, we use a 'cleanup' to emit fake.use
+  // calls for local variables.
+  if (CGM.getCodeGenOpts().ExtendLifetimes &&
+    hasScalarEvaluationKind(D.getType())) {
+    EHStack.pushCleanup<FakeUse>(NormalFakeUse, emission.getAllocatedAddress());
+  }
 
   return emission;
 }
@@ -1979,6 +1994,15 @@
   return LifetimeEndFn;
 }
 
+/// Lazily declare the @llvm.fake.use intrinsic.
+llvm::Constant *CodeGenModule::getLLVMFakeUseFn() {
+  if (FakeUseFn)
+    return FakeUseFn;
+  FakeUseFn =
+      llvm::Intrinsic::getDeclaration(&getModule(), llvm::Intrinsic::fake_use);
+  return FakeUseFn;
+}
+
 namespace {
   /// A cleanup to perform a release of an object at the end of a
   /// function.  This is used to balance out the incoming +1 of a
@@ -2152,6 +2176,21 @@
 
   setAddrOfLocalVar(&D, DeclPtr);
 
+  // Push a FakeUse 'cleanup' object onto the EHStack for the parameter,
+  // which may be the 'this' pointer. This causes the emission of a fake.use
+  // call with the parameter as argument at the end of the function.
+  if (CurFuncDecl && !CurFuncDecl->isImplicit() &&
+      (&D == CXXABIThisDecl || !D.isImplicit())) {
+    if (CGM.getCodeGenOpts().ExtendLifetimes) {
+      if (IsScalar)
+        EHStack.pushCleanup<FakeUse>(NormalFakeUse, DeclPtr);
+    } else if (CGM.getCodeGenOpts().ExtendThisPtr) {
+      // Enter only the 'this' pointer for -fextend-this-ptr.
+      if (&D == CXXABIThisDecl)
+        EHStack.pushCleanup<FakeUse>(NormalFakeUse, DeclPtr);
+    }
+  }
+
   // Emit debug info for param declaration.
   if (CGDebugInfo *DI = getDebugInfo()) {
     if (CGM.getCodeGenOpts().getDebugInfo() >=
Index: lib/CodeGen/CGCleanup.h
===================================================================
--- lib/CodeGen/CGCleanup.h
+++ lib/CodeGen/CGCleanup.h
@@ -77,6 +77,9 @@
     /// Whether this cleanup is a lifetime marker
     unsigned IsLifetimeMarker : 1;
 
+    /// Whether this cleanup is a fake use
+    unsigned IsFakeUse : 1;
+
     /// Whether the normal cleanup should test the activation flag.
     unsigned TestFlagInNormalCleanup : 1;
 
@@ -321,6 +324,9 @@
   bool isLifetimeMarker() const { return CleanupBits.IsLifetimeMarker; }
   void setLifetimeMarker() { CleanupBits.IsLifetimeMarker = true; }
 
+  bool isFakeUse() const { return CleanupBits.IsFakeUse; }
+  void setFakeUse() { CleanupBits.IsFakeUse = true; }
+
   bool hasActiveFlag() const { return ActiveFlag != nullptr; }
   Address getActiveFlag() const {
     return Address(ActiveFlag, CharUnits::One());
Index: lib/CodeGen/CGCleanup.cpp
===================================================================
--- lib/CodeGen/CGCleanup.cpp
+++ lib/CodeGen/CGCleanup.cpp
@@ -149,7 +149,8 @@
     EHScopeStack::stable_iterator Old) const {
   for (EHScopeStack::iterator it = begin(); stabilize(it) != Old; it++) {
     EHCleanupScope *cleanup = dyn_cast<EHCleanupScope>(&*it);
-    if (!cleanup || !cleanup->isLifetimeMarker())
+    // Augment this test to also ignore fake use "cleanups".
+    if (!cleanup || !(cleanup->isLifetimeMarker() || cleanup->isFakeUse()))
       return false;
   }
 
@@ -188,6 +189,7 @@
   bool IsEHCleanup = Kind & EHCleanup;
   bool IsActive = !(Kind & InactiveCleanup);
   bool IsLifetimeMarker = Kind & LifetimeMarker;
+  bool IsFakeUse = Kind & FakeUse;
   EHCleanupScope *Scope =
     new (Buffer) EHCleanupScope(IsNormalCleanup,
                                 IsEHCleanup,
@@ -202,6 +204,8 @@
     InnermostEHScope = stable_begin();
   if (IsLifetimeMarker)
     Scope->setLifetimeMarker();
+  if (IsFakeUse)
+    Scope->setFakeUse();
 
   return Scope->getCleanupBuffer();
 }
Index: lib/CodeGen/CGCall.cpp
===================================================================
--- lib/CodeGen/CGCall.cpp
+++ lib/CodeGen/CGCall.cpp
@@ -2757,6 +2757,14 @@
          II != IE; ++II) {
       if (llvm::IntrinsicInst *Intrinsic =
               dyn_cast<llvm::IntrinsicInst>(&*II)) {
+        // Ignore fake uses and the instructions that load their
+        // operands.
+        if (Intrinsic->getIntrinsicID() == llvm::Intrinsic::fake_use) {
+          const llvm::Value *FakeUseArg = Intrinsic->getArgOperand(0);
+          if (++II == IE || &*II != FakeUseArg)
+            break;
+          continue;
+        }
         if (Intrinsic->getIntrinsicID() == llvm::Intrinsic::lifetime_end) {
           const llvm::Value *CastAddr = Intrinsic->getArgOperand(1);
           ++II;
Index: include/clang/Frontend/CodeGenOptions.def
===================================================================
--- include/clang/Frontend/CodeGenOptions.def
+++ include/clang/Frontend/CodeGenOptions.def
@@ -309,6 +309,12 @@
 /// The default TLS model to use.
 ENUM_CODEGENOPT(DefaultTLSModel, TLSModel, 2, GeneralDynamicTLSModel)
 
+/// Whether to extend the live range of the this ptr.
+CODEGENOPT(ExtendThisPtr, 1, 0)
+
+/// Whether to extend the live range of all local variables.
+CODEGENOPT(ExtendLifetimes, 1, 0)
+
 /// Number of path components to strip when emitting checks. (0 == full
 /// filename)
 VALUE_CODEGENOPT(EmitCheckPathComponentsToStrip, 32, 0)
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -1758,11 +1758,16 @@
  Flags<[CC1Option]>, HelpText<"Place each data in its own section (ELF Only)">;
 def fno_data_sections : Flag <["-"], "fno-data-sections">, Group<f_Group>,
   Flags<[CC1Option]>;
+def fextend_this_ptr : Flag <["-"], "fextend-this-ptr">, Group<f_Group>,
+  HelpText<"Extend the lifetime of the 'this' pointer to improve visibility "
+           "in optimized debugging">, Flags<[CC1Option]>;
+def fextend_lifetimes : Flag <["-"], "fextend-lifetimes">, Group<f_Group>,
+  HelpText<"Extend the lifetimes of local variables and parameters to improve "
+           "visibility in optimized debugging">, Flags<[CC1Option]>;
 def fstack_size_section : Flag<["-"], "fstack-size-section">, Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Emit section containing metadata on function stack sizes">;
 def fno_stack_size_section : Flag<["-"], "fno-stack-size-section">, Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Don't emit section containing metadata on function stack sizes">;
-
 def funique_section_names : Flag <["-"], "funique-section-names">,
   Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Use unique names for text and data sections (ELF Only)">;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to