diff options
| author | mingmingl <mingmingl@google.com> | 2025-11-11 09:36:50 -0800 |
|---|---|---|
| committer | mingmingl <mingmingl@google.com> | 2025-11-11 09:36:50 -0800 |
| commit | 51d8b13194fe401d9e1f4ea09ba226483a0a5a3d (patch) | |
| tree | 4ee1c2c501fc6397169790675be8d40ee4a212f8 | |
| parent | eb1297910e361628719c1c5d81e60ca4e32fb64f (diff) | |
Introduce intrinsic for vtable profilingusers/mingmingl-llvm/intrinsic
| -rw-r--r-- | clang/lib/CodeGen/CGClass.cpp | 22 | ||||
| -rw-r--r-- | llvm/include/llvm/Analysis/IndirectCallVisitor.h | 23 | ||||
| -rw-r--r-- | llvm/include/llvm/IR/Intrinsics.td | 5 | ||||
| -rw-r--r-- | llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp | 39 | ||||
| -rw-r--r-- | llvm/test/Transforms/Inline/update_invoke_prof.ll | 20 | ||||
| -rw-r--r-- | llvm/test/Transforms/Inline/update_value_profile.ll | 8 | ||||
| -rw-r--r-- | llvm/test/Transforms/PGOProfile/vtable_profile.ll | 37 |
7 files changed, 131 insertions, 23 deletions
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 8346ee3aa6a8..71b6873ec0db 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2769,12 +2769,15 @@ LeastDerivedClassWithSameLayout(const CXXRecordDecl *RD) { void CodeGenFunction::EmitTypeMetadataCodeForVCall(const CXXRecordDecl *RD, llvm::Value *VTable, SourceLocation Loc) { + const bool EmitTypeTests = (CGM.getCodeGenOpts().WholeProgramVTables && + // Don't insert type test assumes if we are + // forcing public visibility. + !CGM.AlwaysHasLTOVisibilityPublic(RD)); + // TODO: Add a new Clang option. + const bool EmitInstrTypeTests = CGM.getCodeGenOpts().hasProfileIRInstr(); if (SanOpts.has(SanitizerKind::CFIVCall)) EmitVTablePtrCheckForCall(RD, VTable, CodeGenFunction::CFITCK_VCall, Loc); - else if (CGM.getCodeGenOpts().WholeProgramVTables && - // Don't insert type test assumes if we are forcing public - // visibility. - !CGM.AlwaysHasLTOVisibilityPublic(RD)) { + else if (EmitTypeTests || EmitInstrTypeTests) { CanQualType Ty = CGM.getContext().getCanonicalTagType(RD); llvm::Metadata *MD = CGM.CreateMetadataIdentifierForType(Ty); llvm::Value *TypeId = @@ -2784,9 +2787,14 @@ void CodeGenFunction::EmitTypeMetadataCodeForVCall(const CXXRecordDecl *RD, // @llvm.type.test(). Otherwise emit @llvm.public.type.test(), which WPD // will convert to @llvm.type.test() if we assert at link time that we have // whole program visibility. - llvm::Intrinsic::ID IID = CGM.HasHiddenLTOVisibility(RD) - ? llvm::Intrinsic::type_test - : llvm::Intrinsic::public_type_test; + llvm::Intrinsic::ID IID; + if (EmitTypeTests) { + IID = CGM.HasHiddenLTOVisibility(RD) ? llvm::Intrinsic::type_test + : llvm::Intrinsic::public_type_test; + } else { + assert(EmitInstrTypeTests && "expected EmitInstrTypeTests"); + IID = llvm::Intrinsic::instr_type_test; + } llvm::Value *TypeTest = Builder.CreateCall(CGM.getIntrinsic(IID), {VTable, TypeId}); Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::assume), TypeTest); diff --git a/llvm/include/llvm/Analysis/IndirectCallVisitor.h b/llvm/include/llvm/Analysis/IndirectCallVisitor.h index 6c424038070d..8a70a428bc2b 100644 --- a/llvm/include/llvm/Analysis/IndirectCallVisitor.h +++ b/llvm/include/llvm/Analysis/IndirectCallVisitor.h @@ -27,6 +27,21 @@ struct PGOIndirectCallVisitor : public InstVisitor<PGOIndirectCallVisitor> { std::vector<Instruction *> ProfiledAddresses; PGOIndirectCallVisitor(InstructionType Type) : Type(Type) {} + static bool isVTableInstr(Instruction *Instr) { + for (User *U : Instr->users()) { + if (llvm::IntrinsicInst *II = llvm::dyn_cast<llvm::IntrinsicInst>(U)) { + llvm::Intrinsic::ID IID = II->getIntrinsicID(); + + if (IID == llvm::Intrinsic::instr_type_test || + IID == llvm::Intrinsic::public_type_test || + IID == llvm::Intrinsic::type_test) { + return true; + } + } + } + return false; + } + // Given an indirect call instruction, try to find the the following pattern // // %vtable = load ptr, ptr %obj @@ -44,6 +59,8 @@ struct PGOIndirectCallVisitor : public InstVisitor<PGOIndirectCallVisitor> { if (LI != nullptr) { Value *FuncPtr = LI->getPointerOperand(); // GEP (or bitcast) Value *VTablePtr = FuncPtr->stripInBoundsConstantOffsets(); + + // https://gcc.godbolt.org/z/hM76EGE17 // FIXME: Add support in the frontend so LLVM type intrinsics are // emitted without LTO. This way, added intrinsics could filter // non-vtable instructions and reduce instrumentation overhead. @@ -54,8 +71,10 @@ struct PGOIndirectCallVisitor : public InstVisitor<PGOIndirectCallVisitor> { // of symbols). So the performance overhead from non-vtable profiled // address is negligible if exists at all. Comparing loaded address // with symbol address guarantees correctness. - if (VTablePtr != nullptr && isa<Instruction>(VTablePtr)) - return cast<Instruction>(VTablePtr); + if (Instruction *VTableInstr = dyn_cast_or_null<Instruction>(VTablePtr)) { + if (isVTableInstr(VTableInstr)) + return VTableInstr; + } } return nullptr; } diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index fb9ea10ac912..bd5fa998a07d 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -2544,6 +2544,11 @@ def int_type_checked_load_relative : DefaultAttrsIntrinsic<[llvm_ptr_ty, llvm_i1 def int_public_type_test : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_ptr_ty, llvm_metadata_ty], [IntrNoMem, IntrSpeculatable]>; +// Test whether a pointer is associated with a type metadata identifier. +// TODO: If the return type isn't used by llvm.assume, make it no return. +def int_instr_type_test : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_ptr_ty, llvm_metadata_ty], + [IntrNoMem, IntrSpeculatable]>; + // Create a branch funnel that implements an indirect call to a limited set of // callees. This needs to be a musttail call. def int_icall_branch_funnel : DefaultAttrsIntrinsic<[], [llvm_vararg_ty], []>; diff --git a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp index 5e7548b0a2fd..ff544e07e657 100644 --- a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp +++ b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp @@ -336,6 +336,9 @@ private: /// Force emitting of name vars for unused functions. void lowerCoverageData(GlobalVariable *CoverageNamesVar); + /// Lower the type test intrinsic. + bool lowerInstrTypeTest(); + /// Replace instrprof.mcdc.tvbitmask.update with a shift and or instruction /// using the index represented by the a temp value into a bitmap. void lowerMCDCTestVectorBitmapUpdate(InstrProfMCDCTVBitmapUpdate *Ins); @@ -931,8 +934,42 @@ static bool containsProfilingIntrinsics(Module &M) { containsIntrinsic(Intrinsic::instrprof_value_profile); } +static void dropTypeTests(Module &M, Function &TypeTestFunc, + bool ShouldDropAll) { + for (Use &U : llvm::make_early_inc_range(TypeTestFunc.uses())) { + auto *CI = cast<CallInst>(U.getUser()); + // Find and erase llvm.assume intrinsics for this llvm.type.test call. + for (Use &CIU : llvm::make_early_inc_range(CI->uses())) + if (auto *Assume = dyn_cast<AssumeInst>(CIU.getUser())) + Assume->eraseFromParent(); + // If the assume was merged with another assume, we might have a use on a + // phi (which will feed the assume). Simply replace the use on the phi + // with "true" and leave the merged assume. + // + // If ShouldDropAll is set, then we we need to update any remaining uses, + // regardless of the instruction type. + if (!CI->use_empty()) { + assert(ShouldDropAll || all_of(CI->users(), [](User *U) -> bool { + return isa<PHINode>(U); + })); + CI->replaceAllUsesWith(ConstantInt::getTrue(M.getContext())); + } + CI->eraseFromParent(); + } +} + +bool InstrLowerer::lowerInstrTypeTest() { + Function *InstrTypeTestFunc = + Intrinsic::getDeclarationIfExists(&M, Intrinsic::instr_type_test); + if (!InstrTypeTestFunc) + return false; + + dropTypeTests(M, *InstrTypeTestFunc, true); + return true; +} + bool InstrLowerer::lower() { - bool MadeChange = false; + bool MadeChange = lowerInstrTypeTest(); bool NeedsRuntimeHook = needsRuntimeHookUnconditionally(TT); if (NeedsRuntimeHook) MadeChange = emitRuntimeHook(); diff --git a/llvm/test/Transforms/Inline/update_invoke_prof.ll b/llvm/test/Transforms/Inline/update_invoke_prof.ll index 12eb7dbf418c..d1977929988a 100644 --- a/llvm/test/Transforms/Inline/update_invoke_prof.ll +++ b/llvm/test/Transforms/Inline/update_invoke_prof.ll @@ -7,6 +7,7 @@ declare i32 @__gxx_personality_v0(...) define void @caller(ptr %func) personality ptr @__gxx_personality_v0 !prof !15 { +; call void @callee(ptr %func), !prof !16 ret void @@ -18,6 +19,8 @@ declare void @callee2(ptr %func) define void @callee(ptr %obj) personality ptr @__gxx_personality_v0 !prof !17 { %vtable = load ptr, ptr %obj, !prof !21 + %test = tail call i1 @llvm.public.type.test(ptr %vtable, metadata !"_ZTS4Base") + tail call void @llvm.assume(i1 %test) %func = load ptr, ptr %vtable invoke void %func() to label %next unwind label %lpad, !prof !18 @@ -39,6 +42,14 @@ ret: ret void } +declare i1 @llvm.public.type.test(ptr, metadata) #1 + +declare void @llvm.assume(i1 noundef) #2 + +attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) } +attributes #2 = { mustprogress nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) } + !llvm.module.flags = !{!1} !1 = !{i32 1, !"ProfileSummary", !2} !2 = !{!3, !4, !5, !6, !7, !8, !9, !10} @@ -65,6 +76,8 @@ ret: ; CHECK-LABEL: define void @caller( ; CHECK-SAME: ptr [[FUNC:%.*]]) personality ptr @__gxx_personality_v0 !prof [[PROF14:![0-9]+]] { ; CHECK-NEXT: [[VTABLE_I:%.*]] = load ptr, ptr [[FUNC]], align 8, !prof [[PROF15:![0-9]+]] +; CHECK-NEXT: [[TEST_I:%.*]] = call i1 @llvm.public.type.test(ptr [[VTABLE_I]], metadata !"_ZTS4Base") +; CHECK-NEXT: call void @llvm.assume(i1 [[TEST_I]]) ; CHECK-NEXT: [[FUNC_I:%.*]] = load ptr, ptr [[VTABLE_I]], align 8 ; CHECK-NEXT: invoke void [[FUNC_I]]() ; CHECK-NEXT: to label %[[NEXT_I:.*]] unwind label %[[LPAD_I:.*]], !prof [[PROF16:![0-9]+]] @@ -74,11 +87,13 @@ ret: ; CHECK: [[CONT_I]]: ; CHECK-NEXT: invoke void @callee2(ptr [[FUNC_I]]) ; CHECK-NEXT: to label %[[CALLEE_EXIT:.*]] unwind label %[[LPAD_I]], !prof [[PROF18:![0-9]+]] -; + ; CHECK-LABEL: define void @callee( ; CHECK-SAME: ptr [[OBJ:%.*]]) personality ptr @__gxx_personality_v0 !prof [[PROF19:![0-9]+]] { ; CHECK-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[OBJ]], align 8, !prof [[PROF20:![0-9]+]] +; CHECK-NEXT: [[TEST:%.*]] = tail call i1 @llvm.public.type.test(ptr [[VTABLE]], metadata !"_ZTS4Base") +; CHECK-NEXT: tail call void @llvm.assume(i1 [[TEST]]) ; CHECK-NEXT: [[FUNC:%.*]] = load ptr, ptr [[VTABLE]], align 8 ; CHECK-NEXT: invoke void [[FUNC]]() ; CHECK-NEXT: to label %[[NEXT:.*]] unwind label %[[LPAD:.*]], !prof [[PROF21:![0-9]+]] @@ -89,6 +104,8 @@ ret: ; CHECK-NEXT: invoke void @callee2(ptr [[FUNC]]) ; CHECK-NEXT: to label %[[RET:.*]] unwind label %[[LPAD]], !prof [[PROF18]] + + ; CHECK: [[PROF14]] = !{!"function_entry_count", i64 1000} ; CHECK: [[PROF15]] = !{!"VP", i32 2, i64 1000, i64 789, i64 600, i64 321, i64 400} ; CHECK: [[PROF16]] = !{!"VP", i32 0, i64 1000, i64 123, i64 600, i64 456, i64 400} @@ -98,3 +115,4 @@ ret: ; CHECK: [[PROF20]] = !{!"VP", i32 2, i64 500, i64 789, i64 300, i64 321, i64 200} ; CHECK: [[PROF21]] = !{!"VP", i32 0, i64 500, i64 123, i64 300, i64 456, i64 200} ; CHECK: [[PROF22]] = !{!"branch_weights", i32 500} + diff --git a/llvm/test/Transforms/Inline/update_value_profile.ll b/llvm/test/Transforms/Inline/update_value_profile.ll index 96aa35fb572d..58de492ae0a6 100644 --- a/llvm/test/Transforms/Inline/update_value_profile.ll +++ b/llvm/test/Transforms/Inline/update_value_profile.ll @@ -9,12 +9,16 @@ define i32 @callee(ptr %0, i32 %1) !prof !19 { ; CHECK-LABEL: define i32 @callee( ; CHECK-SAME: ptr [[TMP0:%.*]], i32 [[TMP1:%.*]]) !prof [[PROF0:![0-9]+]] { ; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TMP0]], align 8, !prof [[PROF1:![0-9]+]] +; CHECK-NEXT: [[TEST:%.*]] = tail call i1 @llvm.type.test(ptr [[TMP3]], metadata !"_ZTS4Base") +; CHECK-NEXT: tail call void @llvm.assume(i1 [[TEST]]) ; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 8 ; CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 ; CHECK-NEXT: [[TMP6:%.*]] = tail call i32 [[TMP5]](ptr [[TMP0]], i32 [[TMP1]]), !prof [[PROF2:![0-9]+]] ; CHECK-NEXT: ret i32 [[TMP6]] ; %3 = load ptr, ptr %0, !prof !15 + %test = tail call i1 @llvm.type.test(ptr %3, metadata !"_ZTS4Base") + tail call void @llvm.assume(i1 %test) %5 = getelementptr inbounds i8, ptr %3, i64 8 %6 = load ptr, ptr %5 %7 = tail call i32 %6(ptr %0, i32 %1), !prof !16 @@ -26,6 +30,8 @@ define i32 @caller1(i32 %0) !prof !17 { ; CHECK-SAME: i32 [[TMP0:%.*]]) !prof [[PROF3:![0-9]+]] { ; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @_Z10createTypei(i32 [[TMP0]]) ; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TMP2]], align 8, !prof [[PROF4:![0-9]+]] +; CHECK-NEXT: [[TEST_I:%.*]] = tail call i1 @llvm.type.test(ptr [[TMP3]], metadata !"_ZTS4Base") +; CHECK-NEXT: tail call void @llvm.assume(i1 [[TEST_I]]) ; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 8 ; CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 ; CHECK-NEXT: [[TMP6:%.*]] = tail call i32 [[TMP5]](ptr [[TMP2]], i32 [[TMP0]]), !prof [[PROF5:![0-9]+]] @@ -41,6 +47,8 @@ define i32 @caller2(i32 %0) !prof !18 { ; CHECK-SAME: i32 [[TMP0:%.*]]) !prof [[PROF6:![0-9]+]] { ; CHECK-NEXT: [[TMP2:%.*]] = tail call ptr @_Z10createTypei(i32 [[TMP0]]) ; CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TMP2]], align 8, !prof [[PROF7:![0-9]+]] +; CHECK-NEXT: [[TEST_I:%.*]] = tail call i1 @llvm.type.test(ptr [[TMP3]], metadata !"_ZTS4Base") +; CHECK-NEXT: tail call void @llvm.assume(i1 [[TEST_I]]) ; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 8 ; CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 ; CHECK-NEXT: [[TMP6:%.*]] = tail call i32 [[TMP5]](ptr [[TMP2]], i32 [[TMP0]]), !prof [[PROF8:![0-9]+]] diff --git a/llvm/test/Transforms/PGOProfile/vtable_profile.ll b/llvm/test/Transforms/PGOProfile/vtable_profile.ll index aae1e2d8b4e4..cdca433bc0d6 100644 --- a/llvm/test/Transforms/PGOProfile/vtable_profile.ll +++ b/llvm/test/Transforms/PGOProfile/vtable_profile.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 ; RUN: opt < %s -passes=pgo-instr-gen -enable-vtable-value-profiling -S 2>&1 | FileCheck %s --check-prefix=GEN --implicit-check-not="VTable value profiling is presently not supported" ; RUN: opt < %s -passes=pgo-instr-gen,instrprof -enable-vtable-value-profiling -S 2>&1 | FileCheck %s --check-prefix=LOWER --implicit-check-not="VTable value profiling is presently not supported" @@ -54,27 +55,35 @@ target triple = "x86_64-unknown-linux-gnu" ; LOWER: $__profvt__ZTV7Derived = comdat nodeduplicate ; LOWER: $"__profvt_vtable_local.ll;_ZTVN12_GLOBAL__N_15Base2E" = comdat nodeduplicate -; LOWER: @__profvt__ZTV7Derived = global { i64, ptr, i32 } { i64 -4576307468236080025, ptr @_ZTV7Derived, i32 48 }, section "__llvm_prf_vtab", comdat, align 8 -; LOWER: @"__profvt_vtable_local.ll;_ZTVN12_GLOBAL__N_15Base2E" = internal global { i64, ptr, i32 } { i64 1419990121885302679, ptr @_ZTVN12_GLOBAL__N_15Base2E, i32 24 }, section "__llvm_prf_vtab", comdat, align 8 -; LOWER: @__llvm_prf_vnm = private constant {{.*}}, section "__llvm_prf_vns", align 1 -; LOWER: @llvm.used = appending global [5 x ptr] [ptr @__profvt__ZTV7Derived, ptr @"__profvt_vtable_local.ll;_ZTVN12_GLOBAL__N_15Base2E", ptr @__llvm_prf_vnodes, ptr @__llvm_prf_nm, ptr @__llvm_prf_vnm], section "llvm.metadata" define i32 @_Z4funci(i32 %a) { + + +; GEN: [[TMP0:%.*]] = ptrtoint ptr %vtable to i64 +; GEN-NEXT: call void @llvm.instrprof.value.profile(ptr @__profn__Z4funci, i64 567090795815895039, i64 [[TMP0]], i32 2, i32 0) + +; GEN: [[TMP2:%.*]] = ptrtoint ptr %vtable2 to i64 +; GEN-NEXT: call void @llvm.instrprof.value.profile(ptr @__profn__Z4funci, i64 567090795815895039, i64 [[TMP2]], i32 2, i32 1) + + +; LOWER: [[TMP1:%.*]] = ptrtoint ptr %vtable to i64 +; LOWER-NEXT: call void @__llvm_profile_instrument_target(i64 [[TMP1]], ptr @__profd__Z4funci, i32 2) + +; LOWER: [[TMP3:%.*]] = ptrtoint ptr %vtable2 to i64 +; LOWER-NEXT: call void @__llvm_profile_instrument_target(i64 [[TMP3]], ptr @__profd__Z4funci, i32 3) + + entry: %call = call ptr @_Z10createTypev() %add.ptr = getelementptr inbounds i8, ptr %call, i64 8 %vtable = load ptr, ptr %add.ptr -; GEN: [[P1:%[0-9]+]] = ptrtoint ptr %vtable to i64 -; GEN: call void @llvm.instrprof.value.profile(ptr @__profn__Z4funci, i64 [[CFGHash:[0-9]+]], i64 [[P1]], i32 2, i32 0) -; LOWER: [[P1:%[0-9]+]] = ptrtoint ptr %vtable to i64 -; LOWER: call void @__llvm_profile_instrument_target(i64 [[P1]], ptr @__profd__Z4funci, i32 2) + %test = tail call i1 @llvm.instr.type.test(ptr %vtable, metadata !"_ZTS4Base") + tail call void @llvm.assume(i1 %test) %vfunc1 = load ptr, ptr %vtable %call1 = call i32 %vfunc1(ptr %add.ptr, i32 %a) %vtable2 = load ptr, ptr %call -; GEN: [[P2:%[0-9]+]] = ptrtoint ptr %vtable2 to i64 -; GEN: call void @llvm.instrprof.value.profile(ptr @__profn__Z4funci, i64 [[CFGHash]], i64 [[P2]], i32 2, i32 1) -; LOWER: [[P2:%[0-9]+]] = ptrtoint ptr %vtable2 to i64 -; LOWER: call void @__llvm_profile_instrument_target(i64 [[P2]], ptr @__profd__Z4funci, i32 3) + %test2 = tail call i1 @llvm.instr.type.test(ptr %vtable2, metadata !"_ZTS4Base") + tail call void @llvm.assume(i1 %test2) %vfunc2 = load ptr, ptr %vtable2 %call4 = call i32 %vfunc2(ptr %call, i32 %a) %add = add nsw i32 %call1, %call4 @@ -85,6 +94,10 @@ declare ptr @_Z10createTypev() declare i32 @_ZN12_GLOBAL__N_15Base25func2Ei(ptr %this, i32 %a) declare i32 @_ZN5Base15func1Ei(ptr, i32) +declare i1 @llvm.instr.type.test(ptr, metadata) + +declare void @llvm.assume(i1 noundef) + !0 = !{i64 16, !"_ZTS5Base1"} !3 = !{i64 16, !"_ZTS7Derived"} !6 = !{i64 40, !7} |
