diff --git a/tests/c/funcptrarg_hasir.c b/tests/c/funcptrarg_hasir.c deleted file mode 100644 index 28ea739c5..000000000 --- a/tests/c/funcptrarg_hasir.c +++ /dev/null @@ -1,50 +0,0 @@ -// ## hits a todo! -// ignore-if: true -// Run-time: -// env-var: YKD_SERIALISE_COMPILATION=1 -// env-var: YKD_LOG=4 -// stderr: -// ... -// FIXME: match the indirect call -// yk-execution: enter-jit-code -// z=4 -// ... - -// Test indirect calls where we have IR for the callee. - -#include -#include -#include -#include -#include - -__attribute__((noinline)) int foo(int a) { - NOOPT_VAL(a); - return a + 1; -} - -int bar(int (*func)(int)) { - int a = func(3); - return a; -} - -int main(int argc, char **argv) { - YkMT *mt = yk_mt_new(NULL); - yk_mt_hot_threshold_set(mt, 0); - YkLocation loc = yk_location_new(); - - int z = 0, i = 4; - NOOPT_VAL(i); - NOOPT_VAL(z); - while (i > 0) { - yk_mt_control_point(mt, &loc); - z = bar(foo); - assert(z == 4); - fprintf(stderr, "z=%d\n", z); - i--; - } - - yk_location_drop(loc); - yk_mt_shutdown(mt); - return (EXIT_SUCCESS); -} diff --git a/tests/c/inline_indirect_call.c b/tests/c/inline_indirect_call.c new file mode 100644 index 000000000..98c755ad1 --- /dev/null +++ b/tests/c/inline_indirect_call.c @@ -0,0 +1,72 @@ +// ignore-if: test "$YK_JITC" = "j2" +// Compiler: +// env-var: YKB_EXTRA_CC_FLAGS=-O2 +// Run-time: +// env-var: YKD_LOG_IR=aot,jit-post-opt +// env-var: YKD_SERIALISE_COMPILATION=1 +// env-var: YKD_LOG=4 +// stderr: +// yk-tracing: start-tracing +// foo 7 +// yk-tracing: stop-tracing +// --- Begin aot --- +// ... +// #[yk_outline] +// func bar(%{{_}}: i{{ty}}) -> i{{ty}}; +// ... +// #[yk_indirect_inline] +// func foo(... +// ... +// %{{_}}: i32 = icall %{{_}}(... +// ... +// --- End aot --- +// ... +// --- Begin jit-post-opt --- +// ... +// %{{1}}: i32 = call @bar(... +// ... +// --- End jit-post-opt --- +// foo 6 +// yk-execution: enter-jit-code +// foo 5 +// foo 4 +// yk-execution: deoptimise ... +// exit + +// Check that an indirect call whose callee is marked `yk_indirect_inline` is +// inlined into the trace (as long as the calee pointer is promoted). + +#include +#include +#include +#include +#include +#include + +__attribute__((noinline, yk_outline)) int bar(int i) { return i + 3; } + +__attribute__((yk_indirect_inline, noinline)) int foo(int i) { return bar(i); } + +int main(int argc, char **argv) { + YkMT *mt = yk_mt_new(NULL); + yk_mt_hot_threshold_set(mt, 0); + YkLocation loc = yk_location_new(); + + int i = 4; + int (*fn)(int) = foo; + + NOOPT_VAL(loc); + NOOPT_VAL(i); + NOOPT_VAL(fn); + while (i > 0) { + yk_mt_control_point(mt, &loc); + int (*fp)(int) = yk_promote((void *) fn); + int x = fp(i); + fprintf(stderr, "foo %d\n", x); + i--; + } + fprintf(stderr, "exit\n"); + yk_location_drop(loc); + yk_mt_shutdown(mt); + return (EXIT_SUCCESS); +} diff --git a/tests/c/indirect_call.c b/tests/c/outline_indirect_call.c similarity index 69% rename from tests/c/indirect_call.c rename to tests/c/outline_indirect_call.c index fe144f346..1bc20270a 100644 --- a/tests/c/indirect_call.c +++ b/tests/c/outline_indirect_call.c @@ -1,17 +1,17 @@ // ignore-if: test "$YK_JITC" = "j2" // Run-time: -// env-var: YKD_LOG_IR=jit-pre-opt +// env-var: YKD_LOG_IR=jit-post-opt // env-var: YKD_SERIALISE_COMPILATION=1 // env-var: YKD_LOG=4 // stderr: // yk-tracing: start-tracing // foo 7 // yk-tracing: stop-tracing -// --- Begin jit-pre-opt --- +// --- Begin jit-post-opt --- // ... -// %{{1}}: i32 = icall %{{2}}(%{{3}}) +// %{{1}}: i32 = icall 0x... // ... -// --- End jit-pre-opt --- +// --- End jit-post-opt --- // foo 6 // yk-execution: enter-jit-code // foo 5 @@ -19,7 +19,10 @@ // yk-execution: deoptimise ... // exit -// Check that indirect calls work. +// Check that an indirect call whose callee isn't marked `yk_indirect_inline` +// is outlined in the trace (even if the callee pointer is promoted). +// +// To have it inlined, the calle would need to be marked `yk_indirect_inline`. #include #include @@ -43,7 +46,8 @@ int main(int argc, char **argv) { NOOPT_VAL(fn); while (i > 0) { yk_mt_control_point(mt, &loc); - int x = fn(i); + int (*fp)(int) = yk_promote((void *) fn); + int x = fp(i); fprintf(stderr, "foo %d\n", x); i--; } diff --git a/ykllvm b/ykllvm index b01bf8ef5..58db46cfc 160000 --- a/ykllvm +++ b/ykllvm @@ -1 +1 @@ -Subproject commit b01bf8ef5a1f67692ece80d7a4ee56c03bc9b413 +Subproject commit 58db46cfc016d6d1bd350ea861c6c5258a674ae2 diff --git a/ykrt/src/compile/jitc_yk/aot_ir.rs b/ykrt/src/compile/jitc_yk/aot_ir.rs index 1d1a6f9f1..80898ef3f 100644 --- a/ykrt/src/compile/jitc_yk/aot_ir.rs +++ b/ykrt/src/compile/jitc_yk/aot_ir.rs @@ -1490,6 +1490,7 @@ pub(crate) struct Func { const FUNCFLAG_OUTLINE: u8 = 1; const FUNCFLAG_IDEMPOTENT: u8 = 1 << 1; +const FUNCFLAG_INDIRECT_INLINE: u8 = 1 << 2; impl Func { pub(crate) fn is_declaration(&self) -> bool { @@ -1504,6 +1505,10 @@ impl Func { self.flags & FUNCFLAG_IDEMPOTENT != 0 } + pub(crate) fn is_indirect_inline(&self) -> bool { + self.flags & FUNCFLAG_INDIRECT_INLINE != 0 + } + /// Return the [BBlock] at the specified index. /// /// # Panics @@ -1570,6 +1575,9 @@ impl fmt::Display for DisplayableFunc<'_> { if self.func_.is_outline() { attrs.push("yk_outline"); } + if self.func_.is_indirect_inline() { + attrs.push("yk_indirect_inline"); + } let attrs = if !attrs.is_empty() { &format!("#[{}]\n", attrs.join(", ")) } else { diff --git a/ykrt/src/compile/jitc_yk/trace_builder.rs b/ykrt/src/compile/jitc_yk/trace_builder.rs index 0d267473f..ad79eae15 100644 --- a/ykrt/src/compile/jitc_yk/trace_builder.rs +++ b/ykrt/src/compile/jitc_yk/trace_builder.rs @@ -309,7 +309,12 @@ impl TraceBuilder { let nextinst = blk.insts.last().unwrap(); self.handle_indirectcall( inst, bid, iidx, ftyidx, callop, args, nextinst, safepoint, - ) + )?; + // FIXME: This `return` assumes that the indirect callee is traceable. If it + // isn't, we shouldn't update the previous block (like in the `Inst::Call` case + // above), but we (currently) have no way of knowing if the (dynamic) callee is + // traceable. At least an assertion will fail if/when we encounter this. + return Ok(Some(None)); } aot_ir::Inst::Store { tgt, val, volatile } => { self.handle_store(bid, iidx, tgt, val, *volatile)