// Hand-C reference for bench_mono_dispatch — INDIRECT POLYMORPHIC variant.
//
// Same structure as bench_mono_indirect.c, but the fnptr is selected
// per-iteration from a small array of FOUR distinct (and genuinely
// different) `foo` implementations. This models a polymorphic class
// hierarchy under hypothetical vdisp / dict-passing: the indirect
// target varies, so the branch predictor cannot lock onto a single
// destination. The four bodies are deliberately distinct (different
// constants and operations) so clang's mergefunc cannot collapse them.
//
// This is the LOWER bound on what mono offers — predictor misses on
// indirect dispatch when the call site sees more than one instance.
//
// IMPORTANT: this binary's stdout does NOT match the monomorphic
// variants — different `foo` semantics produce a different final acc.
// The harness skips the equality check for this binary; only the
// timing matters here.
//
// Build: clang -O2 -o bench_mono_indirect_polymorphic bench_mono_indirect_polymorphic.c

#include <stdio.h>
#include <stdint.h>

static int64_t foo_a(int64_t x) __attribute__((noinline));
static int64_t foo_b(int64_t x) __attribute__((noinline));
static int64_t foo_c(int64_t x) __attribute__((noinline));
static int64_t foo_d(int64_t x) __attribute__((noinline));

// Four genuinely different bodies — different constants, different
// op shapes. Same instruction count and roughly same cost so the
// "average" body cost matches the monomorphic case; what differs
// is the dispatch shape.
static int64_t foo_a(int64_t x) { return x * 1103515245 + 12345; }
static int64_t foo_b(int64_t x) { return x * 1664525     + 1013904223; }
static int64_t foo_c(int64_t x) { return x * 22695477    + 1; }
static int64_t foo_d(int64_t x) { return x * 214013      + 2531011; }

typedef int64_t (*foo_fn_t)(int64_t);

static int64_t loop_call(foo_fn_t * volatile fps, int64_t i, int64_t acc) {
    while (i != 0) {
        // i & 3 cycles through the 4 fnptrs every iteration.
        foo_fn_t fp = fps[i & 3];
        acc = acc + fp(acc + i);
        i = i - 1;
    }
    return acc;
}

int main(void) {
    foo_fn_t fps[4] = { &foo_a, &foo_b, &foo_c, &foo_d };
    foo_fn_t * volatile fps_v = fps;
    printf("%lld\n", (long long)loop_call(fps_v, 100000000, 0));
    return 0;
}
