https://github.com/nikic created 
https://github.com/llvm/llvm-project/pull/225072

Clang currently emits calls to llvm.strip.invariant.group under 
`-fstrict-vtable-pointers` whenever a pointer to a dynamic object is used in a 
comparison or might be used in a comparison through operations we cannot track.

The purpose of the llvm.strip.invariant.group intrinsic is to make sure that if 
we have a dominating condition like `a == launder.invariant.group(a)`, we don't 
end up replacing the latter with the former, as that would allow the optimizer 
to assume that the memory stays invariant, despite going through a launder 
operation (e.g. on placement new).

However, since this was introduced, we've come to the understanding that 
replacing pointers based on equality comparison is generally only legal if they 
have the same provenance, and stopped doing such replacements. The specific 
strip.invariant.group/launder.invariant.group case was still buggy due to an 
implementation bug, but this was fixed in 
https://github.com/llvm/llvm-project/pull/224281.

As such, I believe that we no longer need to emit llvm.strip.invariant.group in 
Clang. I plan to also remove the LLVM intrinsic in a followup.

>From 905a1720175a6a9da5db3b07b9a0d9c3a48de21e Mon Sep 17 00:00:00 2001
From: Nikita Popov <[email protected]>
Date: Mon, 21 Sep 2026 11:31:47 +0200
Subject: [PATCH] [Clang] Stop using strip.invariant.group

Clang currently emits calls to llvm.strip.invariant.group under
`-fstrict-vtable-pointers` whenever a pointer to a dynamic object
is used in a comparison or could subsequently be used in a
comparison.
---
 clang/lib/CodeGen/CGBuilder.h                 |  6 ---
 clang/lib/CodeGen/CGExpr.cpp                  | 12 -----
 clang/lib/CodeGen/CGExprScalar.cpp            | 39 +---------------
 .../CodeGenCXX/strict-vtable-pointers.cpp     | 46 ++++++-------------
 4 files changed, 15 insertions(+), 88 deletions(-)

diff --git a/clang/lib/CodeGen/CGBuilder.h b/clang/lib/CodeGen/CGBuilder.h
index 4a51f6be8578f..4519f7160b341 100644
--- a/clang/lib/CodeGen/CGBuilder.h
+++ b/clang/lib/CodeGen/CGBuilder.h
@@ -470,12 +470,6 @@ class CGBuilderTy : public CGBuilderBaseTy {
     
Addr.replaceBasePointer(CreateLaunderInvariantGroup(Addr.getBasePointer()));
     return Addr;
   }
-
-  using CGBuilderBaseTy::CreateStripInvariantGroup;
-  Address CreateStripInvariantGroup(Address Addr) {
-    Addr.replaceBasePointer(CreateStripInvariantGroup(Addr.getBasePointer()));
-    return Addr;
-  }
 };
 
 } // end namespace CodeGen
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index eba802e187beb..c93d3fd8c562e 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5974,18 +5974,6 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, 
const FieldDecl *field,
   Address addr = base.getAddress();
   if (hasBPFPreserveStaticOffset(rec))
     addr = wrapWithBPFPreserveStaticOffset(*this, addr);
-  if (auto *ClassDef = dyn_cast<CXXRecordDecl>(rec)) {
-    if (CGM.getCodeGenOpts().StrictVTablePointers &&
-        ClassDef->isDynamicClass()) {
-      // Getting to any field of dynamic object requires stripping dynamic
-      // information provided by invariant.group.  This is because accessing
-      // fields may leak the real address of dynamic object, which could result
-      // in miscompilation when leaked pointer would be compared.
-      auto *stripped =
-          Builder.CreateStripInvariantGroup(addr.emitRawPointer(*this));
-      addr = Address(stripped, addr.getElementType(), addr.getAlignment());
-    }
-  }
 
   unsigned RecordCVR = base.getVRQualifiers();
   if (rec->isUnion()) {
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index d264f4fb28bd6..43dfd02d4733f 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2717,14 +2717,6 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
         // Casting to pointer that could carry dynamic information (provided by
         // invariant.group) requires launder.
         Src = Builder.CreateLaunderInvariantGroup(Src);
-      } else if (SrcType.mayBeDynamicClass() && DestTy.mayBeNotDynamicClass()) 
{
-        // Casting to pointer that does not carry dynamic information (provided
-        // by invariant.group) requires stripping it.  Note that we don't do it
-        // if the source could not be dynamic type and destination could be
-        // dynamic because dynamic information is already laundered.  It is
-        // because launder(strip(src)) == launder(src), so there is no need to
-        // add extra strip before launder.
-        Src = Builder.CreateStripInvariantGroup(Src);
       }
     }
 
@@ -3007,18 +2999,8 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
   }
   case CK_PointerToIntegral: {
     assert(!DestTy->isBooleanType() && "bool should use PointerToBool");
-    auto *PtrExpr = Visit(E);
-
-    if (CGF.CGM.getCodeGenOpts().StrictVTablePointers) {
-      const QualType SrcType = E->getType();
-
-      // Casting to integer requires stripping dynamic information as it does
-      // not carries it.
-      if (SrcType.mayBeDynamicClass())
-        PtrExpr = Builder.CreateStripInvariantGroup(PtrExpr);
-    }
-
-    PtrExpr = CGF.authPointerToPointerCast(PtrExpr, E->getType(), DestTy);
+    auto *PtrExpr =
+        CGF.authPointerToPointerCast(Visit(E), E->getType(), DestTy);
     return Builder.CreatePtrToInt(PtrExpr, ConvertType(DestTy));
   }
   case CK_ToVoid: {
@@ -5380,23 +5362,6 @@ Value *ScalarExprEmitter::EmitCompare(const 
BinaryOperator *E,
       Result = Builder.CreateICmp(SICmpOpc, LHS, RHS, "cmp");
     } else {
       // Unsigned integers and pointers.
-
-      if (CGF.CGM.getCodeGenOpts().StrictVTablePointers &&
-          !isa<llvm::ConstantPointerNull>(LHS) &&
-          !isa<llvm::ConstantPointerNull>(RHS)) {
-
-        // Dynamic information is required to be stripped for comparisons,
-        // because it could leak the dynamic information.  Based on comparisons
-        // of pointers to dynamic objects, the optimizer can replace one 
pointer
-        // with another, which might be incorrect in presence of invariant
-        // groups. Comparison with null is safe because null does not carry any
-        // dynamic information.
-        if (LHSTy.mayBeDynamicClass())
-          LHS = Builder.CreateStripInvariantGroup(LHS);
-        if (RHSTy.mayBeDynamicClass())
-          RHS = Builder.CreateStripInvariantGroup(RHS);
-      }
-
       Result = Builder.CreateICmp(UICmpOpc, LHS, RHS, "cmp");
     }
 
diff --git a/clang/test/CodeGenCXX/strict-vtable-pointers.cpp 
b/clang/test/CodeGenCXX/strict-vtable-pointers.cpp
index ade81725487c9..85a16f571e8d0 100644
--- a/clang/test/CodeGenCXX/strict-vtable-pointers.cpp
+++ b/clang/test/CodeGenCXX/strict-vtable-pointers.cpp
@@ -287,18 +287,14 @@ void compare() {
   // CHECK-NEW: call ptr @llvm.launder.invariant.group.p0(ptr
   A *b = new (a) B;
 
-  // CHECK-NEW: %[[a:.*]] = call ptr @llvm.strip.invariant.group.p0(ptr
-  // CHECK-NEW: %[[b:.*]] = call ptr @llvm.strip.invariant.group.p0(ptr
-  // CHECK-NEW: %cmp = icmp eq ptr %[[a]], %[[b]]
+  // CHECK-NEW-NOT: call ptr @llvm.strip.invariant.group
   if (a == b)
     b->foo();
 }
 
 // CHECK-NEW-LABEL: compare2
 bool compare2(A *a, A *a2) {
-  // CHECK-NEW: %[[a:.*]] = call ptr @llvm.strip.invariant.group.p0(ptr
-  // CHECK-NEW: %[[b:.*]] = call ptr @llvm.strip.invariant.group.p0(ptr
-  // CHECK-NEW: %cmp = icmp ult ptr %[[a]], %[[b]]
+  // CHECK-NEW-NOT: call ptr @llvm.strip.invariant.group
   return a < a2;
 }
 // CHECK-NEW-LABEL: compareIntPointers
@@ -330,13 +326,9 @@ bool compareNull(A *a) {
 }
 
 struct X;
-// We have to also introduce the barriers if comparing pointers to incomplete
-// objects
 // CHECK-NEW-LABEL: define{{.*}} zeroext i1 @_Z8compare4P1XS0_
 bool compare4(X *x, X *x2) {
-  // CHECK-NEW: %[[x:.*]] = call ptr @llvm.strip.invariant.group.p0(ptr
-  // CHECK-NEW: %[[x2:.*]] = call ptr @llvm.strip.invariant.group.p0(ptr
-  // CHECK-NEW: %cmp = icmp eq ptr %[[x]], %[[x2]]
+  // CHECK-NEW-NOT: call ptr @llvm.strip.invariant.group
   return x == x2;
 }
 
@@ -349,7 +341,7 @@ void member1(HoldingOtherVirtuals *p) {
 
 // CHECK-NEW-LABEL: member2
 void member2(A *a) {
-  // CHECK-NEW: call ptr @llvm.strip.invariant.group.p0
+  // CHECK-NEW-NOT: call ptr @llvm.strip.invariant.group.p0
   (void)a->m;
 }
 
@@ -366,16 +358,14 @@ void testCompareMembers() {
   // CHECK-NEW:   call void %{{.*}}(ptr {{[^,]*}} %{{.*}})
   ap->foo();
   // CHECK-NEW:    [[TMP7:%.*]] = load ptr, ptr [[AP]]
-  // CHECK-NEW:    [[TMP9:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr 
[[TMP7]])
-  // CHECK-NEW:    [[M:%.*]] = getelementptr inbounds nuw [[STRUCT_A:%.*]], 
ptr [[TMP9]], i32 0, i32 1
+  // CHECK-NEW:    [[M:%.*]] = getelementptr inbounds nuw [[STRUCT_A:%.*]], 
ptr [[TMP7]], i32 0, i32 1
   // CHECK-NEW:    store ptr [[M]], ptr [[APM]]
   int *const apm = &ap->m;
 
   B *bp = new (ap) B;
 
   // CHECK-NEW:    [[TMP20:%.*]] = load ptr, ptr [[BP]]
-  // CHECK-NEW:    [[TMP23:%.*]] = call ptr @llvm.strip.invariant.group.p0(ptr 
[[TMP20]])
-  // CHECK-NEW:    [[M4:%.*]] = getelementptr inbounds nuw [[STRUCT_A]], ptr 
[[TMP23]], i32 0, i32 1
+  // CHECK-NEW:    [[M4:%.*]] = getelementptr inbounds nuw [[STRUCT_A]], ptr 
[[TMP20]], i32 0, i32 1
   // CHECK-NEW:    store ptr [[M4]], ptr [[BPM]]
   int *const bpm = &bp->m;
 
@@ -391,36 +381,26 @@ void testCompareMembers() {
 
 // CHECK-NEW-LABEL: define{{.*}} void @_Z9testCast1P1A(ptr
 void testCast1(A *a) {
-  // Here we get rid of dynamic info
-  // CHECK-NEW: call ptr @llvm.strip.invariant.group
+  // CHECK-NEW-NOT: call ptr @llvm.strip.invariant.group
+  // CHECK-NEW-NOT: @llvm.launder.invariant.group
   auto *v = (void *)a;
 
-  // CHECK-NEW: call ptr @llvm.strip.invariant.group
   auto i2 = (uintptr_t)a;
   (void)i2;
 
-  // CHECK-NEW-NOT: @llvm.strip.invariant.group
-  // CHECK-NEW-NOT: @llvm.launder.invariant.group
-
-  // The information is already stripped
   auto i = (uintptr_t)v;
 }
 
 struct Incomplete;
 // CHECK-NEW-LABEL: define{{.*}} void @_Z9testCast2P10Incomplete(ptr
 void testCast2(Incomplete *I) {
-  // Here we get rid of potential dynamic info
-  // CHECK-NEW: call ptr @llvm.strip.invariant.group
+  // CHECK-NEW-NOT: @llvm.strip.invariant.group
+  // CHECK-NEW-NOT: @llvm.launder.invariant.group
   auto *v = (void *)I;
 
-  // CHECK-NEW: call ptr @llvm.strip.invariant.group
   auto i2 = (uintptr_t)I;
   (void)i2;
 
-  // CHECK-NEW-NOT: @llvm.strip.invariant.group
-  // CHECK-NEW-NOT: @llvm.launder.invariant.group
-
-  // The information is already stripped
   auto i = (uintptr_t)v;
 }
 
@@ -470,14 +450,14 @@ void testCast5(B *b) {
 // CHECK-NEW-LABEL: define{{.*}} void @_Z9testCast6P1A(
 void testCast6(A *a) {
 
-  // CHECK-NEW: @llvm.strip.invariant.group
+  // CHECK-NEW-NOT: @llvm.strip.invariant.group
   auto *I = (Incomplete *)a;
   (void)I;
   // CHECK-NEW: @llvm.launder.invariant.group
   auto *a2 = (A *)I;
   (void)a2;
 
-  // CHECK-NEW: @llvm.strip.invariant.group
+  // CHECK-NEW-NOT: @llvm.strip.invariant.group
   auto *E = (Empty *)a;
   (void)E;
 
@@ -531,7 +511,7 @@ void testCast8(Incomplete *I) {
 
 // CHECK-NEW-LABEL: define{{.*}} void @_Z9testCast9
 void testCast9(PossiblyDerivingFromDynamicBase<Incomplete> *P) {
-  // CHECK-NEW: @llvm.strip.invariant.group
+  // CHECK-NEW-NOT: @llvm.strip.invariant.group
   auto *V = (void *)P;
 
   // CHECK-NEW-LABEL: ret void

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to