This patch adds Clang support for speculative devirtualization and integrates the related pass into the pass pipeline. It's building on the LLVM backend implementation from PR #159048. Speculative devirtualization transforms an indirect call (the virtual function) to a guarded direct call. It is guarded by a comparison of the virtual function pointer to the expected target. This optimization is still safe without LTO because it doesn't do direct calls, it's conditional according to the function ptr. This optimization: - Opt-in: Disabled by default, enabled via `-fdevirtualize-speculatively` - Works in non-LTO mode - Handles publicly-visible objects. - Uses guarded devirtualization with fallback to indirect calls when the speculation is incorrect. For this C++ example: ``` class Base { public: __attribute__((noinline)) virtual void virtual_function1() { asm volatile("NOP"); } virtual void virtual_function2() { asm volatile("NOP"); } }; class Derived : public Base { public: void virtual_function2() override { asm volatile("NOP"); } }; __attribute__((noinline)) void foo(Base *BV) { BV->virtual_function1(); } void bar() { Base *b = new Derived(); foo(b); } ``` Here is the IR without enabling speculative devirtualization: ``` define dso_local void @_Z3fooP4Base(ptr noundef %BV) local_unnamed_addr #0 { entry: %vtable = load ptr, ptr %BV, align 8, !tbaa !6 %0 = load ptr, ptr %vtable, align 8 tail call void %0(ptr noundef nonnull align 8 dereferenceable(8) %BV) ret void } ``` IR after enabling speculative devirtualization: ``` define dso_local void @_Z3fooP4Base(ptr noundef %BV) local_unnamed_addr #0 { entry: %vtable = load ptr, ptr %BV, align 8, !tbaa !12 %0 = load ptr, ptr %vtable, align 8 %1 = icmp eq ptr %0, @_ZN4Base17virtual_function1Ev br i1 %1, label %if.true.direct_targ, label %if.false.orig_indirect, !prof !15 if.true.direct_targ: ; preds = %entry tail call void @_ZN4Base17virtual_function1Ev(ptr noundef nonnull align 8 dereferenceable(8) %BV) br label %if.end.icp if.false.orig_indirect: ; preds = %entry tail call void %0(ptr noundef nonnull align 8 dereferenceable(8) %BV) br label %if.end.icp if.end.icp: ; preds = %if.false.orig_indirect, %if.true.direct_targ ret void } ```
79 lines
1.3 KiB
C++
79 lines
1.3 KiB
C++
// Test that Clang emits vtable metadata when speculative devirtualization is enabled.
|
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fdevirtualize-speculatively -emit-llvm -o - %s | FileCheck --check-prefix=CHECK %s
|
|
|
|
struct A {
|
|
A();
|
|
virtual void f();
|
|
};
|
|
|
|
struct B : virtual A {
|
|
B();
|
|
virtual void g();
|
|
virtual void h();
|
|
};
|
|
|
|
namespace {
|
|
|
|
struct D : B {
|
|
D();
|
|
virtual void f();
|
|
virtual void h();
|
|
};
|
|
|
|
}
|
|
|
|
A::A() {}
|
|
B::B() {}
|
|
D::D() {}
|
|
|
|
void A::f() {
|
|
}
|
|
|
|
void B::g() {
|
|
}
|
|
|
|
void D::f() {
|
|
}
|
|
|
|
void D::h() {
|
|
}
|
|
|
|
void af(A *a) {
|
|
// CHECK: [[P:%[^ ]*]] = call i1 @llvm.public.type.test(ptr [[VT:%[^ ]*]], metadata !"_ZTS1A")
|
|
// CHECK-NEXT: call void @llvm.assume(i1 [[P]])
|
|
a->f();
|
|
}
|
|
|
|
void dg1(D *d) {
|
|
// CHECK: [[P:%[^ ]*]] = call i1 @llvm.public.type.test(ptr [[VT:%[^ ]*]], metadata !"_ZTS1B")
|
|
// CHECK-NEXT: call void @llvm.assume(i1 [[P]])
|
|
d->g();
|
|
}
|
|
|
|
void df1(D *d) {
|
|
// CHECK: [[P:%[^ ]*]] = call i1 @llvm.type.test(ptr [[VT:%[^ ]*]], metadata !11)
|
|
// CHECK-NEXT: call void @llvm.assume(i1 [[P]])
|
|
d->f();
|
|
}
|
|
|
|
void dh1(D *d) {
|
|
// CHECK: [[P:%[^ ]*]] = call i1 @llvm.type.test(ptr [[VT:%[^ ]*]], metadata !11)
|
|
// CHECK-NEXT: call void @llvm.assume(i1 [[P]])
|
|
d->h();
|
|
}
|
|
|
|
|
|
D d;
|
|
|
|
void foo() {
|
|
dg1(&d);
|
|
df1(&d);
|
|
dh1(&d);
|
|
|
|
|
|
struct FA : A {
|
|
void f() {}
|
|
} fa;
|
|
af(&fa);
|
|
}
|