// Hand-C reference for bench_mono_dispatch — INDIRECT (fnptr) variant. // // Same algorithm as bench_mono_direct_noinline.c, but `foo` is reached // through a function pointer assigned at runtime. This represents the // "virtual dispatch" / "dictionary-passing" lower bound: the call // target is opaque to the optimiser at compile time, so clang cannot // inline and emits an indirect call (`callq *`). // // The fnptr is assigned from main via a volatile-pointer indirection // to defeat any speculative devirtualisation clang -O2 might attempt // (without PGO it does not devirt monomorphic-target indirect calls, // but the volatile guard makes that explicit and stable across clang // versions). // // `foo` keeps `noinline` so we measure dispatch-shape, not body size. // The pair (direct_noinline vs indirect) isolates exactly the // indirect-call cost on this hardware. // // Build: clang -O2 -o bench_mono_indirect bench_mono_indirect.c #include #include static int64_t foo(int64_t x) __attribute__((noinline)); static int64_t foo(int64_t x) { return x * 1103515245 + 12345; } typedef int64_t (*foo_fn_t)(int64_t); static int64_t loop_call(foo_fn_t fp, int64_t i, int64_t acc) { while (i != 0) { acc = acc + fp(acc + i); i = i - 1; } return acc; } int main(void) { // Volatile barrier so the optimiser sees the target as // dynamically-determined — it cannot prove the fnptr is // monomorphic at the call site without PGO. foo_fn_t volatile fp_v = &foo; foo_fn_t fp = fp_v; printf("%lld\n", (long long)loop_call(fp, 100000000, 0)); return 0; }