[clang] [llvm] [WPD] Support speculative devirtualization with LTO (PR #233380)

via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 20 05:54:37 PDT 2026


================
@@ -1131,12 +1120,6 @@ bool DevirtModule::tryFindVirtualCallTargets(
     if (Fn->getName() == "__cxa_pure_virtual")
       continue;
 
-    // In most cases empty functions will be overridden by the
-    // implementation of the derived class, so we can skip them.
-    if (DevirtSpeculatively && Fn->getReturnType()->isVoidTy() &&
----------------
KRM7 wrote:

There aren't speculative/non-speculative modes after the changes, both could be performed in the same module, and this function is shared by the speculative and single-impl cases, so it has to work for both. For example, consider something like this:
```ll
@vtable.empty = linkonce_odr hidden constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr null, ptr @empty] }, !type !0, !vcall_visibility !1
@vtable.nonempty = linkonce_odr hidden constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr null, ptr @nonempty] }, !type !0, !vcall_visibility !1

define void @empty(ptr %this) { ; ignored in tryFindVirtualCallTargets()
  ret void
}

declare void @f()
define void @nonempty(ptr %this) {
  call void @f()
  ret void
}

define void @test(ptr %p) {
entry:
  %vtable = load ptr, ptr %p
  %ok = call i1 @llvm.type.test(ptr %vtable, metadata !"T")
  call void @llvm.assume(i1 %ok)
  %fn = load ptr, ptr %vtable
  call void %fn(ptr %p) ; devirtualized to call @nonempty
  ret void
}

declare i1 @llvm.type.test(ptr, metadata)
declare void @llvm.assume(i1)

!0 = !{i64 16, !"T"}
!1 = !{i64 1}
```

With the check and speculative devirtualization enabled, the `@empty` function would be ignored, and the indirect call in `@test` would be replaced with an unguarded direct call to `@nonempty`, which is wrong. 

https://github.com/llvm/llvm-project/pull/233380


More information about the cfe-commits mailing list