From 9e57bd0020222a669ea701b76f480b1f73b35605 Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Thu, 8 Oct 2026 13:35:14 -0400 Subject: [PATCH 1/3] [jit-interface] Fix --mode=dyn case --- src/engine/x86-64/X86_64Target.v3 | 23 ++++---- test/regress.failures.x86-64-linux.spc | 1 + .../ext:jit-interface/func_new_loop.bin.wast | 14 +++++ .../ext:jit-interface/func_new_loop.wast | 20 +++++++ test/regress/ext:jit-interface/gen.main.v3 | 57 +++++++++++++++++++ test/regress/ext:jit-interface/gen.sh | 4 +- 6 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 test/regress/ext:jit-interface/func_new_loop.bin.wast create mode 100644 test/regress/ext:jit-interface/func_new_loop.wast diff --git a/src/engine/x86-64/X86_64Target.v3 b/src/engine/x86-64/X86_64Target.v3 index 33af71f0a..3e676a04a 100644 --- a/src/engine/x86-64/X86_64Target.v3 +++ b/src/engine/x86-64/X86_64Target.v3 @@ -253,13 +253,7 @@ class X86_64SpcStrategy extends X86_64ExecutionStrategy { if (!applyJitFilter(wf.instance.module, wf.decl, how)) return SpcResultForStub(wf, X86_64Spc.setInterpreterFallback(wf.decl), null); var module = wf.instance.module; - // A module that was never prepared for this target has no code region yet. This - // happens for the inner module of an environment, whose functions are created at - // runtime by {func.new}; allocate a region for it on demand. - if (module.target_module.spc_code == null) { - allocateCodeForModule(module, MINIMUM_CODE_SIZE + X86_64Spc.estimateCodeSizeFor(wf.decl)); - } - var code = module.target_module.spc_code; + var code = ensureCodeForModule(module, wf.decl); var compiler = newCompiler(module.filename); // XXX: cache per-thread var masm = X86_64MacroAssembler.!(compiler.masm), w = masm.asm.w; @@ -420,7 +414,7 @@ class X86_64DynamicStrategy extends X86_64SpcStrategy { return TargetOsrInfo(Pointer.NULL, null); } var label = compiler.genOsr(module, wf.decl, pc, null); - var code = module.target_module.spc_code; + var code = ensureCodeForModule(module, wf.decl); var masm = X86_64MacroAssembler.!(compiler.masm); var codeSize = masm.w.end(); if (label == null || !code.hasSpaceFor(codeSize)) { @@ -462,6 +456,13 @@ def allocateCodeForModule(module: Module, codeSize: int) { module.filename, (mapping.range.start - Pointer.NULL), (mapping.range.end - Pointer.NULL)).ln(); } +// Returns the code region for {module}, allocating one if necessary. +def ensureCodeForModule(module: Module, func: FuncDecl) -> X86_64SpcModuleCode { + if (module.target_module.spc_code == null) { + allocateCodeForModule(module, MINIMUM_CODE_SIZE + X86_64Spc.estimateCodeSizeFor(func)); + } + return module.target_module.spc_code; +} def applyJitFilter(module: Module, func: FuncDecl, how: string) -> bool { var filter = CompilerOptions.JIT_FILTER.get(); if (filter == null || filter.matches(module, func)) return true; @@ -469,12 +470,10 @@ def applyJitFilter(module: Module, func: FuncDecl, how: string) -> bool { return false; } -def RDTSC_FUNC = X86_64PreGenFunc.new("rdtsc", null, genRdtsc); - -def genRdtsc(ic: X86_64InterpreterCode, w: DataWriter) { +def RDTSC_FUNC = X86_64PreGenFunc.new("rdtsc", null, fun (ic: X86_64InterpreterCode, w: DataWriter) { def asm = X86_64Assemblers.create64(w); asm.rdtsc(); asm.q.shl_r_i(X86_64Regs.RDX, 32); asm.q.or_r_r(X86_64Regs.RAX, X86_64Regs.RDX); // XXX: use V3 return register symbolic constant asm.ret(); -} +}); diff --git a/test/regress.failures.x86-64-linux.spc b/test/regress.failures.x86-64-linux.spc index df0506994..61137f542 100644 --- a/test/regress.failures.x86-64-linux.spc +++ b/test/regress.failures.x86-64-linux.spc @@ -30,6 +30,7 @@ test/regress/ext:legacy-eh/try_delegate5.bin.wast test/regress/ext:jit-interface/func_new0.bin.wast test/regress/ext:jit-interface/func_new_args.bin.wast test/regress/ext:jit-interface/func_new_invalid_env.bin.wast +test/regress/ext:jit-interface/func_new_loop.bin.wast test/regress/ext:jit-interface/func_new_scope_func.bin.wast test/regress/ext:jit-interface/func_new_scope_global.bin.wast test/regress/ext:jit-interface/func_new_scope_memory.bin.wast diff --git a/test/regress/ext:jit-interface/func_new_loop.bin.wast b/test/regress/ext:jit-interface/func_new_loop.bin.wast new file mode 100644 index 000000000..45eb55fb9 --- /dev/null +++ b/test/regress/ext:jit-interface/func_new_loop.bin.wast @@ -0,0 +1,14 @@ +(module binary + "\00\61\73\6d\01\00\00\00\01\8b\80\80\80\00\02\60" + "\01\7f\01\7f\60\01\7f\01\7f\03\82\80\80\80\00\01" + "\00\05\83\80\80\80\00\01\00\01\07\88\80\80\80\00" + "\01\04\6d\61\69\6e\00\00\0e\83\80\80\80\00\01\00" + "\00\0a\92\80\80\80\00\01\10\00\20\00\41\10\41\21" + "\fb\27\00\00\01\00\14\01\0b\0b\a7\80\80\80\00\01" + "\00\41\10\0b\21\01\01\7f\02\40\03\40\20\00\45\0d" + "\01\20\01\20\00\6a\21\01\20\00\41\01\6b\21\00\0c" + "\00\0b\0b\20\01\0b" +) +(assert_return (invoke "main" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "main" (i32.const 100)) (i32.const 5050)) +(assert_return (invoke "main" (i32.const 100000)) (i32.const 705082704)) diff --git a/test/regress/ext:jit-interface/func_new_loop.wast b/test/regress/ext:jit-interface/func_new_loop.wast new file mode 100644 index 000000000..2de105784 --- /dev/null +++ b/test/regress/ext:jit-interface/func_new_loop.wast @@ -0,0 +1,20 @@ +(module + (type $ft (func (param i32) (result i32))) + (memory 1) + (data (i32.const 16) "\01\01\7f\02\40\03\40\20\00\45\0d\01\20\01\20\00\6a\21\01\20\00\41\01\6b\21\00\0c\00\0b\0b\20\01\0b") + ;; (func (param $n i32) (result i32) (local $sum i32) + ;; (block (loop + ;; (br_if 1 (i32.eqz (local.get $n))) + ;; (local.set $sum (i32.add (local.get $sum) (local.get $n))) + ;; (local.set $n (i32.sub (local.get $n) (i32.const 1))) + ;; (br 0))) + ;; (local.get $sum)) + (scope $s) + (func (export "main") (param i32) (result i32) + (call_ref $ft (local.get 0) + (func.new 0 $ft $s (i32.const 16) (i32.const 33))) + ) +) +(assert_return (invoke "main" (i32.const 0)) (i32.const 0)) +(assert_return (invoke "main" (i32.const 100)) (i32.const 5050)) +(assert_return (invoke "main" (i32.const 100000)) (i32.const 705082704)) diff --git a/test/regress/ext:jit-interface/gen.main.v3 b/test/regress/ext:jit-interface/gen.main.v3 index daf517457..1521205c5 100644 --- a/test/regress/ext:jit-interface/gen.main.v3 +++ b/test/regress/ext:jit-interface/gen.main.v3 @@ -48,6 +48,9 @@ def i32_const(val: int) -> Array { b.put(I32C).put_s32leb(val); return b.extract(); } +def op(o: Opcode) -> byte { + return u8.!(o.code); +} def cat(a: Array, b: Array) -> Array { return Arrays.concat(a, b); } @@ -232,6 +235,7 @@ def genAll() { gen_func_new_trap_oob(); gen_func_new_trap_invalid(); gen_func_new_invalid_env(); + gen_func_new_loop(); } // The simplest case: compile a constant function out of memory and call it. @@ -479,3 +483,56 @@ def gen_func_new_invalid_env() { ")" ], "unknown environment", m.finish()); } + +// A generated function that runs a long loop, so that a tiering strategy compiles it while +// it is running (OSR) or after enough calls. Such a function belongs to the environment's +// instance, not the enclosing one, so compiling it must not assume the enclosing module's +// code space. Regression test for an OSR crash in --mode=dyn. +def gen_func_new_loop() { + var loop_code: Array = [ + op(Opcode.BLOCK), BpTypeCode.EmptyBlock.code, + op(Opcode.LOOP), BpTypeCode.EmptyBlock.code, + op(Opcode.LOCAL_GET), 0, op(Opcode.I32_EQZ), op(Opcode.BR_IF), 1, + op(Opcode.LOCAL_GET), 1, op(Opcode.LOCAL_GET), 0, op(Opcode.I32_ADD), op(Opcode.LOCAL_SET), 1, + op(Opcode.LOCAL_GET), 0, I32C, 1, op(Opcode.I32_SUB), op(Opcode.LOCAL_SET), 0, + op(Opcode.BR), 0, + END, END, + op(Opcode.LOCAL_GET), 1, END]; + var gen = cat([1, 1, BpTypeCode.I32.code], loop_code); // one local declaration group: 1 x i32 + var main_code = cat( + [u8.!(Opcode.LOCAL_GET.code), 0], + newAndCall(gen.length)); + var m = ModuleGen.new() + .types([(I, I), (I, I)]) + .funcs([0]) + .memory(1) + .exportFunc("main", 0) + .environs([[]]) + .code([body(main_code)]) + .data([(CODE_ADDR, gen)]); + var hex = StringBuilder.new(); + for (x in gen) hex.putc('\\').putc(Chars.hexMap_l[x >> 4]).putc(Chars.hexMap_l[x & 0xF]); + add("func_new_loop", [ + "(module", + " (type $ft (func (param i32) (result i32)))", + " (memory 1)", + Strings.format1(" (data (i32.const 16) \"%s\")", hex.toString()), + " ;; (func (param $n i32) (result i32) (local $sum i32)", + " ;; (block (loop", + " ;; (br_if 1 (i32.eqz (local.get $n)))", + " ;; (local.set $sum (i32.add (local.get $sum) (local.get $n)))", + " ;; (local.set $n (i32.sub (local.get $n) (i32.const 1)))", + " ;; (br 0)))", + " ;; (local.get $sum))", + " (scope $s)", + " (func (export \"main\") (param i32) (result i32)", + " (call_ref $ft (local.get 0)", + Strings.format1(" (func.new 0 $ft $s (i32.const 16) (i32.const %d)))", gen.length), + " )", + ")" + ], [ + "(assert_return (invoke \"main\" (i32.const 0)) (i32.const 0))", + "(assert_return (invoke \"main\" (i32.const 100)) (i32.const 5050))", + "(assert_return (invoke \"main\" (i32.const 100000)) (i32.const 705082704))" + ], m.finish()); +} diff --git a/test/regress/ext:jit-interface/gen.sh b/test/regress/ext:jit-interface/gen.sh index 214a4e8fd..0c33ba1e0 100755 --- a/test/regress/ext:jit-interface/gen.sh +++ b/test/regress/ext:jit-interface/gen.sh @@ -33,10 +33,12 @@ cd $HERE # Mirrors the source set that build.sh uses for the v3i target, plus BinBuilder, which is # reused from the unit tests to encode sections and patch their LEB sizes. -exec $V3I -fun-exprs -simple-bodies \ +exec $V3I -lang:fun-exprs -lang:simple-bodies -lang:descriptors \ $VIRGIL_LIB_UTIL/*.v3 \ $WIZENG_LOC/src/engine/*.v3 \ $WIZENG_LOC/src/engine/compression/*.v3 \ + $WIZENG_LOC/src/engine/objmodel/*.v3 \ + $WIZENG_LOC/src/engine/objmodel/boxed/*.v3 \ $WIZENG_LOC/src/engine/v3/*.v3 \ $WIZENG_LOC/src/util/*.v3 \ $WIZENG_LOC/test/unittest/BinBuilder.v3 \ From ed29739dbd7f494fa4c71900dc6ebe2633487c93 Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Thu, 8 Oct 2026 14:51:28 -0400 Subject: [PATCH 2/3] More tests --- .../func_new_many_args.bin.wast | 22 +++++++++++++++++++ .../ext:jit-interface/func_new_many_args.wast | 17 ++++++++++++++ .../func_new_mixed_args.bin.wast | 22 +++++++++++++++++++ .../func_new_mixed_args.wast | 15 +++++++++++++ .../func_new_multi_result.bin.wast | 14 ++++++++++++ .../func_new_multi_result.wast | 17 ++++++++++++++ 6 files changed, 107 insertions(+) create mode 100644 test/regress/ext:jit-interface/func_new_many_args.bin.wast create mode 100644 test/regress/ext:jit-interface/func_new_many_args.wast create mode 100644 test/regress/ext:jit-interface/func_new_mixed_args.bin.wast create mode 100644 test/regress/ext:jit-interface/func_new_mixed_args.wast create mode 100644 test/regress/ext:jit-interface/func_new_multi_result.bin.wast create mode 100644 test/regress/ext:jit-interface/func_new_multi_result.wast diff --git a/test/regress/ext:jit-interface/func_new_many_args.bin.wast b/test/regress/ext:jit-interface/func_new_many_args.bin.wast new file mode 100644 index 000000000..6630b8637 --- /dev/null +++ b/test/regress/ext:jit-interface/func_new_many_args.bin.wast @@ -0,0 +1,22 @@ +(module binary + "\00\61\73\6d\01\00\00\00\01\a9\80\80\80\00\02\60" + "\10\7f\7f\7f\7f\7f\7f\7f\7f\7f\7f\7f\7f\7f\7f\7f" + "\7f\01\7f\60\10\7f\7f\7f\7f\7f\7f\7f\7f\7f\7f\7f" + "\7f\7f\7f\7f\7f\01\7f\03\82\80\80\80\00\01\00\05" + "\83\80\80\80\00\01\00\01\07\88\80\80\80\00\01\04" + "\6d\61\69\6e\00\00\0e\83\80\80\80\00\01\00\00\0a" + "\b1\80\80\80\00\01\2f\00\20\00\20\01\20\02\20\03" + "\20\04\20\05\20\06\20\07\20\08\20\09\20\0a\20\0b" + "\20\0c\20\0d\20\0e\20\0f\41\10\41\de\00\fb\27\00" + "\00\01\00\14\01\0b\0b\e4\80\80\80\00\01\00\41\10" + "\0b\5e\00\20\00\41\1f\6c\20\01\6a\41\1f\6c\20\02" + "\6a\41\1f\6c\20\03\6a\41\1f\6c\20\04\6a\41\1f\6c" + "\20\05\6a\41\1f\6c\20\06\6a\41\1f\6c\20\07\6a\41" + "\1f\6c\20\08\6a\41\1f\6c\20\09\6a\41\1f\6c\20\0a" + "\6a\41\1f\6c\20\0b\6a\41\1f\6c\20\0c\6a\41\1f\6c" + "\20\0d\6a\41\1f\6c\20\0e\6a\41\1f\6c\20\0f\6a\0b" +) +(assert_return (invoke "main" (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "main" (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 7)) (i32.const 7)) +(assert_return (invoke "main" (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4) (i32.const 5) (i32.const 6) (i32.const 7) (i32.const 8) (i32.const 9) (i32.const 10) (i32.const 11) (i32.const 12) (i32.const 13) (i32.const 14) (i32.const 15) (i32.const 16)) (i32.const -1270509304)) +(assert_return (invoke "main" (i32.const 16) (i32.const 15) (i32.const 14) (i32.const 13) (i32.const 12) (i32.const 11) (i32.const 10) (i32.const 9) (i32.const 8) (i32.const 7) (i32.const 6) (i32.const 5) (i32.const 4) (i32.const 3) (i32.const 2) (i32.const 1)) (i32.const 319397880)) diff --git a/test/regress/ext:jit-interface/func_new_many_args.wast b/test/regress/ext:jit-interface/func_new_many_args.wast new file mode 100644 index 000000000..030561bca --- /dev/null +++ b/test/regress/ext:jit-interface/func_new_many_args.wast @@ -0,0 +1,17 @@ +(module + (type $ft (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32))) + (memory 1) + (data (i32.const 16) "\00\20\00\41\1f\6c\20\01\6a\41\1f\6c\20\02\6a\41\1f\6c\20\03\6a\41\1f\6c\20\04\6a\41\1f\6c\20\05\6a\41\1f\6c\20\06\6a\41\1f\6c\20\07\6a\41\1f\6c\20\08\6a\41\1f\6c\20\09\6a\41\1f\6c\20\0a\6a\41\1f\6c\20\0b\6a\41\1f\6c\20\0c\6a\41\1f\6c\20\0d\6a\41\1f\6c\20\0e\6a\41\1f\6c\20\0f\6a\0b") + ;; (func (param $p0 i32) ... (param $p15 i32) (result i32) + ;; acc = p0; acc = acc * 31 + p[i] for i in 1..15 + ;; ...) + (scope $s) + (func (export "main") (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result i32) + (call_ref $ft (local.get 0) (local.get 1) (local.get 2) (local.get 3) (local.get 4) (local.get 5) (local.get 6) (local.get 7) (local.get 8) (local.get 9) (local.get 10) (local.get 11) (local.get 12) (local.get 13) (local.get 14) (local.get 15) + (func.new 0 $ft $s (i32.const 16) (i32.const 94))) + ) +) +(assert_return (invoke "main" (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0)) (i32.const 0)) +(assert_return (invoke "main" (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 7)) (i32.const 7)) +(assert_return (invoke "main" (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4) (i32.const 5) (i32.const 6) (i32.const 7) (i32.const 8) (i32.const 9) (i32.const 10) (i32.const 11) (i32.const 12) (i32.const 13) (i32.const 14) (i32.const 15) (i32.const 16)) (i32.const -1270509304)) +(assert_return (invoke "main" (i32.const 16) (i32.const 15) (i32.const 14) (i32.const 13) (i32.const 12) (i32.const 11) (i32.const 10) (i32.const 9) (i32.const 8) (i32.const 7) (i32.const 6) (i32.const 5) (i32.const 4) (i32.const 3) (i32.const 2) (i32.const 1)) (i32.const 319397880)) diff --git a/test/regress/ext:jit-interface/func_new_mixed_args.bin.wast b/test/regress/ext:jit-interface/func_new_mixed_args.bin.wast new file mode 100644 index 000000000..7e6f4ba45 --- /dev/null +++ b/test/regress/ext:jit-interface/func_new_mixed_args.bin.wast @@ -0,0 +1,22 @@ +(module binary + "\00\61\73\6d\01\00\00\00\01\a9\80\80\80\00\02\60" + "\10\7f\7e\7d\7c\7f\7e\7d\7c\7f\7e\7d\7c\7f\7e\7d" + "\7c\01\7c\60\10\7f\7e\7d\7c\7f\7e\7d\7c\7f\7e\7d" + "\7c\7f\7e\7d\7c\01\7c\03\82\80\80\80\00\01\00\05" + "\83\80\80\80\00\01\00\01\07\88\80\80\80\00\01\04" + "\6d\61\69\6e\00\00\0e\83\80\80\80\00\01\00\00\0a" + "\b1\80\80\80\00\01\2f\00\20\00\20\01\20\02\20\03" + "\20\04\20\05\20\06\20\07\20\08\20\09\20\0a\20\0b" + "\20\0c\20\0d\20\0e\20\0f\41\10\41\fd\00\fb\27\00" + "\00\01\00\14\01\0b\0b\83\81\80\80\00\01\00\41\10" + "\0b\7d\00\20\00\b7\41\01\b7\a2\20\01\b9\41\02\b7" + "\a2\a0\20\02\bb\41\03\b7\a2\a0\20\03\41\04\b7\a2" + "\a0\20\04\b7\41\05\b7\a2\a0\20\05\b9\41\06\b7\a2" + "\a0\20\06\bb\41\07\b7\a2\a0\20\07\41\08\b7\a2\a0" + "\20\08\b7\41\09\b7\a2\a0\20\09\b9\41\0a\b7\a2\a0" + "\20\0a\bb\41\0b\b7\a2\a0\20\0b\41\0c\b7\a2\a0\20" + "\0c\b7\41\0d\b7\a2\a0\20\0d\b9\41\0e\b7\a2\a0\20" + "\0e\bb\41\0f\b7\a2\a0\20\0f\41\10\b7\a2\a0\0b" +) +(assert_return (invoke "main" (i32.const 1) (i64.const 1) (f32.const 0x1p+0) (f64.const 0x1p+0) (i32.const 1) (i64.const 1) (f32.const 0x1p+0) (f64.const 0x1p+0) (i32.const 1) (i64.const 1) (f32.const 0x1p+0) (f64.const 0x1p+0) (i32.const 1) (i64.const 1) (f32.const 0x1p+0) (f64.const 0x1p+0)) (f64.const 0x1.1p+7)) +(assert_return (invoke "main" (i32.const -1) (i64.const 1000000000000) (f32.const 0x1p-1) (f64.const -0x1p-2) (i32.const 2) (i64.const -3) (f32.const 0x1.8p+0) (f64.const 0x1.2a05f2p+33) (i32.const 0) (i64.const 0) (f32.const 0x0p+0) (f64.const 0x0p+0) (i32.const 0) (i64.const 0) (f32.const 0x0p+0) (f64.const 0x1p-3)) (f64.const 0x1.e449a94004p+40)) diff --git a/test/regress/ext:jit-interface/func_new_mixed_args.wast b/test/regress/ext:jit-interface/func_new_mixed_args.wast new file mode 100644 index 000000000..984d6a0fc --- /dev/null +++ b/test/regress/ext:jit-interface/func_new_mixed_args.wast @@ -0,0 +1,15 @@ +(module + (type $ft (func (param i32 i64 f32 f64 i32 i64 f32 f64 i32 i64 f32 f64 i32 i64 f32 f64) (result f64))) + (memory 1) + (data (i32.const 16) "\00\20\00\b7\41\01\b7\a2\20\01\b9\41\02\b7\a2\a0\20\02\bb\41\03\b7\a2\a0\20\03\41\04\b7\a2\a0\20\04\b7\41\05\b7\a2\a0\20\05\b9\41\06\b7\a2\a0\20\06\bb\41\07\b7\a2\a0\20\07\41\08\b7\a2\a0\20\08\b7\41\09\b7\a2\a0\20\09\b9\41\0a\b7\a2\a0\20\0a\bb\41\0b\b7\a2\a0\20\0b\41\0c\b7\a2\a0\20\0c\b7\41\0d\b7\a2\a0\20\0d\b9\41\0e\b7\a2\a0\20\0e\bb\41\0f\b7\a2\a0\20\0f\41\10\b7\a2\a0\0b") + ;; (func (param i32 i64 f32 f64 i32 i64 f32 f64 i32 i64 f32 f64 i32 i64 f32 f64) (result f64) + ;; sum of f64(p[i]) * (i + 1) for i in 0..15 + ;; ...) + (scope $s) + (func (export "main") (param i32 i64 f32 f64 i32 i64 f32 f64 i32 i64 f32 f64 i32 i64 f32 f64) (result f64) + (call_ref $ft (local.get 0) (local.get 1) (local.get 2) (local.get 3) (local.get 4) (local.get 5) (local.get 6) (local.get 7) (local.get 8) (local.get 9) (local.get 10) (local.get 11) (local.get 12) (local.get 13) (local.get 14) (local.get 15) + (func.new 0 $ft $s (i32.const 16) (i32.const 125))) + ) +) +(assert_return (invoke "main" (i32.const 1) (i64.const 1) (f32.const 0x1p+0) (f64.const 0x1p+0) (i32.const 1) (i64.const 1) (f32.const 0x1p+0) (f64.const 0x1p+0) (i32.const 1) (i64.const 1) (f32.const 0x1p+0) (f64.const 0x1p+0) (i32.const 1) (i64.const 1) (f32.const 0x1p+0) (f64.const 0x1p+0)) (f64.const 0x1.1p+7)) +(assert_return (invoke "main" (i32.const -1) (i64.const 1000000000000) (f32.const 0x1p-1) (f64.const -0x1p-2) (i32.const 2) (i64.const -3) (f32.const 0x1.8p+0) (f64.const 0x1.2a05f2p+33) (i32.const 0) (i64.const 0) (f32.const 0x0p+0) (f64.const 0x0p+0) (i32.const 0) (i64.const 0) (f32.const 0x0p+0) (f64.const 0x1p-3)) (f64.const 0x1.e449a94004p+40)) diff --git a/test/regress/ext:jit-interface/func_new_multi_result.bin.wast b/test/regress/ext:jit-interface/func_new_multi_result.bin.wast new file mode 100644 index 000000000..9d28168bc --- /dev/null +++ b/test/regress/ext:jit-interface/func_new_multi_result.bin.wast @@ -0,0 +1,14 @@ +(module binary + "\00\61\73\6d\01\00\00\00\01\97\80\80\80\00\02\60" + "\04\7f\7e\7d\7c\04\7c\7d\7e\7f\60\04\7f\7e\7d\7c" + "\04\7c\7d\7e\7f\03\82\80\80\80\00\01\00\05\83\80" + "\80\80\00\01\00\01\07\88\80\80\80\00\01\04\6d\61" + "\69\6e\00\00\0e\83\80\80\80\00\01\00\00\0a\98\80" + "\80\80\00\01\16\00\20\00\20\01\20\02\20\03\41\10" + "\41\11\fb\27\00\00\01\00\14\01\0b\0b\97\80\80\80" + "\00\01\00\41\10\0b\11\00\20\03\20\02\20\01\41\01" + "\ac\7c\20\00\41\03\6c\0b" +) +(assert_return (invoke "main" (i32.const 5) (i64.const 41) (f32.const 0x1.4p+1) (f64.const -0x1.fp+2)) (f64.const -0x1.fp+2) (f32.const 0x1.4p+1) (i64.const 42) (i32.const 15)) +(assert_return (invoke "main" (i32.const -2) (i64.const -1) (f32.const -0x0p+0) (f64.const 0x1.7e43c8800759cp+996)) (f64.const 0x1.7e43c8800759cp+996) (f32.const -0x0p+0) (i64.const 0) (i32.const -6)) +(assert_return (invoke "main" (i32.const 0x7fffffff) (i64.const 0x7fffffffffffffff) (f32.const 0x1p-149) (f64.const 0x1p-1074)) (f64.const 0x1p-1074) (f32.const 0x1p-149) (i64.const 0x8000000000000000) (i32.const 0x7ffffffd)) diff --git a/test/regress/ext:jit-interface/func_new_multi_result.wast b/test/regress/ext:jit-interface/func_new_multi_result.wast new file mode 100644 index 000000000..f4e1b7295 --- /dev/null +++ b/test/regress/ext:jit-interface/func_new_multi_result.wast @@ -0,0 +1,17 @@ +(module + (type $ft (func (param i32 i64 f32 f64) (result f64 f32 i64 i32))) + (memory 1) + (data (i32.const 16) "\00\20\03\20\02\20\01\41\01\ac\7c\20\00\41\03\6c\0b") + ;; (func (param $a i32) (param $b i64) (param $c f32) (param $d f64) (result f64 f32 i64 i32) + ;; (local.get $d) (local.get $c) + ;; (i64.add (local.get $b) (i64.extend_i32_s (i32.const 1))) + ;; (i32.mul (local.get $a) (i32.const 3))) + (scope $s) + (func (export "main") (param i32 i64 f32 f64) (result f64 f32 i64 i32) + (call_ref $ft (local.get 0) (local.get 1) (local.get 2) (local.get 3) + (func.new 0 $ft $s (i32.const 16) (i32.const 17))) + ) +) +(assert_return (invoke "main" (i32.const 5) (i64.const 41) (f32.const 0x1.4p+1) (f64.const -0x1.fp+2)) (f64.const -0x1.fp+2) (f32.const 0x1.4p+1) (i64.const 42) (i32.const 15)) +(assert_return (invoke "main" (i32.const -2) (i64.const -1) (f32.const -0x0p+0) (f64.const 0x1.7e43c8800759cp+996)) (f64.const 0x1.7e43c8800759cp+996) (f32.const -0x0p+0) (i64.const 0) (i32.const -6)) +(assert_return (invoke "main" (i32.const 0x7fffffff) (i64.const 0x7fffffffffffffff) (f32.const 0x1p-149) (f64.const 0x1p-1074)) (f64.const 0x1p-1074) (f32.const 0x1p-149) (i64.const 0x8000000000000000) (i32.const 0x7ffffffd)) From 322b49a7e14ee252d8cb77a7958e51e25641d5a3 Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Thu, 8 Oct 2026 15:00:26 -0400 Subject: [PATCH 3/3] Add SPC support for func.new --- src/engine/Type.v3 | 1 + src/engine/compiler/SinglePassCompiler.v3 | 28 +++++++++++++++++++++++ src/engine/x86-64/X86_64MacroAssembler.v3 | 1 + test/regress.failures.x86-64-linux.spc | 10 -------- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/engine/Type.v3 b/src/engine/Type.v3 index 654c808d1..fb40e4c13 100644 --- a/src/engine/Type.v3 +++ b/src/engine/Type.v3 @@ -98,6 +98,7 @@ component ValueTypes { def ONE_STRUCTREF_TYPE: Array = [STRUCTREF]; def ONE_ARRAYREF_TYPE: Array = [ARRAYREF]; def ONE_CONTREF_TYPE: Array = [CONTREF]; + def ONE_FUNCREF_TYPE: Array = [FUNCREF_NONNULL]; // Helper utility for a final signature type with no supertypes. def newSig = SigDecl.new(true, NO_HEAPTYPES, _, _); diff --git a/src/engine/compiler/SinglePassCompiler.v3 b/src/engine/compiler/SinglePassCompiler.v3 index 14980cf5f..2cc16eb49 100644 --- a/src/engine/compiler/SinglePassCompiler.v3 +++ b/src/engine/compiler/SinglePassCompiler.v3 @@ -1944,6 +1944,9 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl def visit_MEMORY_FILL(mindex: u31) { emit_call_runtime_op1(Opcode.MEMORY_FILL, mindex, true); } + def visit_FUNC_NEW(mem_index: u31, sig_index: u31, env_index: u31) { + emit_call_runtime_op3n(Opcode.FUNC_NEW, mem_index, sig_index, env_index, 2, ValueTypes.ONE_FUNCREF_TYPE, true); + } def visit_TABLE_INIT(eindex: u31, tindex: u31) { emit_call_runtime_op2(Opcode.TABLE_INIT, eindex, tindex, true); } @@ -2034,6 +2037,31 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl emit_reload_regs(); if (!runtimeSpillMode.free_regs) state.emitRestoreAll(resolver); } + def emit_call_runtime_op3n(op: Opcode, arg1: u31, arg2: u31, arg3: u31, args: u32, results: Array, canTrap: bool) { + state.emitSaveAll(resolver, runtimeSpillMode); + emit_compute_vsp(regs.vsp, state.sp); + emit_spill_vsp(regs.vsp); + masm.emit_store_curstack_vsp(regs.vsp); + + def emit = fun { + masm.emit_get_curstack(regs.runtime_arg0); + masm.emit_v3_set_NativeStack_rsp_r_r(regs.runtime_arg0, regs.sp); + masm.emit_push_NativeStack_rsp_r_r(regs.runtime_arg0); + emit_load_instance(regs.runtime_arg1); + masm.emit_mov_r_i(regs.runtime_arg2, arg1); + masm.emit_mov_r_i(regs.runtime_arg3, arg2); + masm.emit_mov_r_i(regs.runtime_arg4, arg3); + masm.emit_call_runtime_op(op); + }; + // Reconstruct stack frames across runtime calls that might (Wasm-level) trap. + if (canTrap) withReconstructedInlinedFrames(emit); else emit(); + masm.emit_get_curstack(regs.scratch); + masm.emit_pop_NativeStack_rsp_r_r(regs.scratch); + dropN(args); + for (t in results) state.push(typeToKindFlags(t) | TAG_STORED | IS_STORED, NO_REG, 0); + emit_reload_regs(); + if (!runtimeSpillMode.free_regs) state.emitRestoreAll(resolver); + } def emitBrIf(sv: SpcVal, cond: MasmBrCond, label: MasmLabel, target: SpcControl, emptyTransfer: bool, repush: BrRepush) { if (sv.isConst()) { var taken = (sv.const == 0) == cond.zero; diff --git a/src/engine/x86-64/X86_64MacroAssembler.v3 b/src/engine/x86-64/X86_64MacroAssembler.v3 index b4043ae6a..657a2de64 100644 --- a/src/engine/x86-64/X86_64MacroAssembler.v3 +++ b/src/engine/x86-64/X86_64MacroAssembler.v3 @@ -893,6 +893,7 @@ class X86_64MacroAssembler extends MacroAssembler { MEMORY_COPY => emit_call_runtime(RT.MEMORY_COPY); MEMORY_FILL => emit_call_runtime(RT.MEMORY_FILL); TABLE_INIT => emit_call_runtime(RT.TABLE_INIT); + FUNC_NEW => emit_call_runtime(RT.FUNC_NEW); TABLE_COPY => emit_call_runtime(RT.TABLE_COPY); TABLE_GROW => emit_call_runtime(RT.TABLE_GROW); TABLE_FILL => emit_call_runtime(RT.TABLE_FILL); diff --git a/test/regress.failures.x86-64-linux.spc b/test/regress.failures.x86-64-linux.spc index 61137f542..d553cd59a 100644 --- a/test/regress.failures.x86-64-linux.spc +++ b/test/regress.failures.x86-64-linux.spc @@ -26,13 +26,3 @@ test/regress/ext:legacy-eh/try_delegate1.bin.wast test/regress/ext:legacy-eh/try_delegate3.bin.wast test/regress/ext:legacy-eh/try_delegate4.bin.wast test/regress/ext:legacy-eh/try_delegate5.bin.wast - -test/regress/ext:jit-interface/func_new0.bin.wast -test/regress/ext:jit-interface/func_new_args.bin.wast -test/regress/ext:jit-interface/func_new_invalid_env.bin.wast -test/regress/ext:jit-interface/func_new_loop.bin.wast -test/regress/ext:jit-interface/func_new_scope_func.bin.wast -test/regress/ext:jit-interface/func_new_scope_global.bin.wast -test/regress/ext:jit-interface/func_new_scope_memory.bin.wast -test/regress/ext:jit-interface/func_new_trap_invalid.bin.wast -test/regress/ext:jit-interface/func_new_trap_oob.bin.wast