summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormingmingl <mingmingl@google.com>2025-11-11 09:36:50 -0800
committermingmingl <mingmingl@google.com>2025-11-11 09:36:50 -0800
commit51d8b13194fe401d9e1f4ea09ba226483a0a5a3d (patch)
tree4ee1c2c501fc6397169790675be8d40ee4a212f8
parenteb1297910e361628719c1c5d81e60ca4e32fb64f (diff)
Introduce intrinsic for vtable profilingusers/mingmingl-llvm/intrinsic
-rw-r--r--clang/lib/CodeGen/CGClass.cpp22
-rw-r--r--llvm/include/llvm/Analysis/IndirectCallVisitor.h23
-rw-r--r--llvm/include/llvm/IR/Intrinsics.td5
-rw-r--r--llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp39
-rw-r--r--llvm/test/Transforms/Inline/update_invoke_prof.ll20
-rw-r--r--llvm/test/Transforms/Inline/update_value_profile.ll8
-rw-r--r--llvm/test/Transforms/PGOProfile/vtable_profile.ll37
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}