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
