diff --git a/docs/example.cton b/docs/example.cton index 493794da50..0cbd77a6cd 100644 --- a/docs/example.cton +++ b/docs/example.cton @@ -1,6 +1,6 @@ test verifier -function %average(i32, i32) -> f32 { +function %average(i32, i32) -> f32 native { ss1 = local 8 ; Stack slot for ``sum``. ebb1(v1: i32, v2: i32): diff --git a/docs/langref.rst b/docs/langref.rst index e7b0cad3d7..f20b5b6e84 100644 --- a/docs/langref.rst +++ b/docs/langref.rst @@ -410,13 +410,6 @@ This simple example illustrates direct function calls and signatures:: Indirect function calls use a signature declared in the preamble. -.. inst:: SIG = signature signature - - Declare a function signature for use with indirect calls. - - :arg signature: Function signature. See :token:`signature`. - :result SIG: A signature identifier. - .. autoinst:: call_indirect .. todo:: Define safe indirect function calls. diff --git a/filetests/isa/intel/abi64.cton b/filetests/isa/intel/abi64.cton index ecd3d29fd6..59f3107560 100644 --- a/filetests/isa/intel/abi64.cton +++ b/filetests/isa/intel/abi64.cton @@ -6,14 +6,14 @@ isa intel ; regex: V=v\d+ function %f() { - sig0 = signature(i32) -> i32 - ; check: sig0 = signature(i32 [%rdi]) -> i32 [%rax] + sig0 = (i32) -> i32 native + ; check: sig0 = (i32 [%rdi]) -> i32 [%rax] native - sig1 = signature(i64) -> b1 - ; check: sig1 = signature(i64 [%rdi]) -> b1 [%rax] + sig1 = (i64) -> b1 native + ; check: sig1 = (i64 [%rdi]) -> b1 [%rax] native - sig2 = signature(f32, i64) -> f64 - ; check: sig2 = signature(f32 [%xmm0], i64 [%rdi]) -> f64 [%xmm0] + sig2 = (f32, i64) -> f64 native + ; check: sig2 = (f32 [%xmm0], i64 [%rdi]) -> f64 [%xmm0] native ebb0: return diff --git a/filetests/isa/intel/binary32.cton b/filetests/isa/intel/binary32.cton index 456623a235..af1bf73a9b 100644 --- a/filetests/isa/intel/binary32.cton +++ b/filetests/isa/intel/binary32.cton @@ -9,7 +9,7 @@ isa intel haswell function %I32() { fn0 = function %foo() - sig0 = signature() + sig0 = () ebb0: ; asm: movl $1, %ecx diff --git a/filetests/isa/intel/binary64.cton b/filetests/isa/intel/binary64.cton index bc19e039d3..fb6f62d928 100644 --- a/filetests/isa/intel/binary64.cton +++ b/filetests/isa/intel/binary64.cton @@ -11,7 +11,7 @@ isa intel haswell ; Tests for i64 instructions. function %I64() { fn0 = function %foo() - sig0 = signature() + sig0 = () ebb0: @@ -457,7 +457,7 @@ ebb2: ; be done by an instruction shrinking pass. function %I32() { fn0 = function %foo() - sig0 = signature() + sig0 = () ebb0: diff --git a/filetests/isa/riscv/abi-e.cton b/filetests/isa/riscv/abi-e.cton index 543b55079f..df06402283 100644 --- a/filetests/isa/riscv/abi-e.cton +++ b/filetests/isa/riscv/abi-e.cton @@ -7,8 +7,8 @@ isa riscv enable_e function %f() { ; Spilling into the stack args after %x15 since %16 and up are not ; available in RV32E. - sig0 = signature(i64, i64, i64, i64) -> i64 - ; check: sig0 = signature(i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13], i32 [%x14], i32 [%x15], i32 [0], i32 [4]) -> i32 [%x10], i32 [%x11] + sig0 = (i64, i64, i64, i64) -> i64 native + ; check: sig0 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13], i32 [%x14], i32 [%x15], i32 [0], i32 [4]) -> i32 [%x10], i32 [%x11] native ebb0: return } diff --git a/filetests/isa/riscv/abi.cton b/filetests/isa/riscv/abi.cton index f26c7686cf..c57c09fd97 100644 --- a/filetests/isa/riscv/abi.cton +++ b/filetests/isa/riscv/abi.cton @@ -5,27 +5,27 @@ isa riscv ; regex: V=v\d+ function %f() { - sig0 = signature(i32) -> i32 - ; check: sig0 = signature(i32 [%x10]) -> i32 [%x10] + sig0 = (i32) -> i32 native + ; check: sig0 = (i32 [%x10]) -> i32 [%x10] native - sig1 = signature(i64) -> b1 - ; check: sig1 = signature(i32 [%x10], i32 [%x11]) -> b1 [%x10] + sig1 = (i64) -> b1 native + ; check: sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] native ; The i64 argument must go in an even-odd register pair. - sig2 = signature(f32, i64) -> f64 - ; check: sig2 = signature(f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] + sig2 = (f32, i64) -> f64 native + ; check: sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] native ; Spilling into the stack args. - sig3 = signature(f64, f64, f64, f64, f64, f64, f64, i64) -> f64 - ; check: sig3 = signature(f64 [%f10], f64 [%f11], f64 [%f12], f64 [%f13], f64 [%f14], f64 [%f15], f64 [%f16], i32 [0], i32 [4]) -> f64 [%f10] + sig3 = (f64, f64, f64, f64, f64, f64, f64, i64) -> f64 native + ; check: sig3 = (f64 [%f10], f64 [%f11], f64 [%f12], f64 [%f13], f64 [%f14], f64 [%f15], f64 [%f16], i32 [0], i32 [4]) -> f64 [%f10] native ; Splitting vectors. - sig4 = signature(i32x4) - ; check: sig4 = signature(i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13]) + sig4 = (i32x4) native + ; check: sig4 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13]) native ; Splitting vectors, then splitting ints. - sig5 = signature(i64x4) - ; check: sig5 = signature(i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13], i32 [%x14], i32 [%x15], i32 [%x16], i32 [%x17]) + sig5 = (i64x4) native + ; check: sig5 = (i32 [%x10], i32 [%x11], i32 [%x12], i32 [%x13], i32 [%x14], i32 [%x15], i32 [%x16], i32 [%x17]) native ebb0: return diff --git a/filetests/isa/riscv/binary32.cton b/filetests/isa/riscv/binary32.cton index 6575609267..1ed2fcabea 100644 --- a/filetests/isa/riscv/binary32.cton +++ b/filetests/isa/riscv/binary32.cton @@ -4,7 +4,7 @@ isa riscv function %RV32I(i32 link [%x1]) -> i32 link [%x1] { fn0 = function %foo() - sig0 = signature() + sig0 = () ebb0(v9999: i32): [-,%x10] v1 = iconst.i32 1 diff --git a/filetests/isa/riscv/legalize-abi.cton b/filetests/isa/riscv/legalize-abi.cton index cb94836ab4..f80494cc1a 100644 --- a/filetests/isa/riscv/legalize-abi.cton +++ b/filetests/isa/riscv/legalize-abi.cton @@ -106,7 +106,7 @@ ebb0(v0: i64x4): } function %indirect(i32) { - sig1 = signature() + sig1 = () native ebb0(v0: i32): call_indirect sig1, v0() return @@ -114,7 +114,7 @@ ebb0(v0: i32): ; The first argument to call_indirect doesn't get altered. function %indirect_arg(i32, f32x2) { - sig1 = signature(f32x2) + sig1 = (f32x2) native ebb0(v0: i32, v1: f32x2): call_indirect sig1, v0(v1) ; check: call_indirect $sig1, $v0($V, $V) diff --git a/filetests/isa/riscv/parse-encoding.cton b/filetests/isa/riscv/parse-encoding.cton index cd02a3ca47..3fdb8f62d6 100644 --- a/filetests/isa/riscv/parse-encoding.cton +++ b/filetests/isa/riscv/parse-encoding.cton @@ -3,32 +3,32 @@ test legalizer isa riscv function %parse_encoding(i32 [%x5]) -> i32 [%x10] { - ; check: function %parse_encoding(i32 [%x5], i32 link [%x1]) -> i32 [%x10], i32 link [%x1] { + ; check: function %parse_encoding(i32 [%x5], i32 link [%x1]) -> i32 [%x10], i32 link [%x1] native { - sig0 = signature(i32 [%x10]) -> i32 [%x10] - ; check: sig0 = signature(i32 [%x10]) -> i32 [%x10] + sig0 = (i32 [%x10]) -> i32 [%x10] native + ; check: sig0 = (i32 [%x10]) -> i32 [%x10] native - sig1 = signature(i32 [%x10], i32 [%x11]) -> b1 [%x10] - ; check: sig1 = signature(i32 [%x10], i32 [%x11]) -> b1 [%x10] + sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] native + ; check: sig1 = (i32 [%x10], i32 [%x11]) -> b1 [%x10] native - sig2 = signature(f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] - ; check: sig2 = signature(f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] + sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] native + ; check: sig2 = (f32 [%f10], i32 [%x12], i32 [%x13]) -> f64 [%f10] native ; Arguments on stack where not necessary - sig3 = signature(f64 [%f10], i32 [0], i32 [4]) -> f64 [%f10] - ; check: sig3 = signature(f64 [%f10], i32 [0], i32 [4]) -> f64 [%f10] + sig3 = (f64 [%f10], i32 [0], i32 [4]) -> f64 [%f10] native + ; check: sig3 = (f64 [%f10], i32 [0], i32 [4]) -> f64 [%f10] native ; Stack argument before register argument - sig4 = signature(f32 [72], i32 [%x10]) - ; check: sig4 = signature(f32 [72], i32 [%x10]) + sig4 = (f32 [72], i32 [%x10]) native + ; check: sig4 = (f32 [72], i32 [%x10]) native ; Return value on stack - sig5 = signature() -> f32 [0] - ; check: sig5 = signature() -> f32 [0] + sig5 = () -> f32 [0] native + ; check: sig5 = () -> f32 [0] native ; function + signature - fn15 = function %bar(i32 [%x10]) -> b1 [%x10] - ; check: sig6 = signature(i32 [%x10]) -> b1 [%x10] + fn15 = function %bar(i32 [%x10]) -> b1 [%x10] native + ; check: sig6 = (i32 [%x10]) -> b1 [%x10] native ; nextln: fn0 = sig6 %bar ebb0(v0: i32): diff --git a/filetests/licm/basic.cton b/filetests/licm/basic.cton index 36b7864cfe..37dda60d2a 100644 --- a/filetests/licm/basic.cton +++ b/filetests/licm/basic.cton @@ -14,7 +14,7 @@ ebb2(v5: i32): return v5 } -; sameln: function %simple_loop(i32) -> i32 { +; sameln: function %simple_loop ; nextln: ebb2(v6: i32): ; nextln: v1 = iconst.i32 1 ; nextln: v2 = iconst.i32 2 diff --git a/filetests/licm/complex.cton b/filetests/licm/complex.cton index b5063d807f..07efb9ff5f 100644 --- a/filetests/licm/complex.cton +++ b/filetests/licm/complex.cton @@ -39,7 +39,7 @@ ebb5(v16: i32): return v17 } -; sameln: function %complex(i32) -> i32 { +; sameln: function %complex ; nextln: ebb6(v20: i32): ; nextln: v1 = iconst.i32 1 ; nextln: v2 = iconst.i32 4 diff --git a/filetests/parser/branch.cton b/filetests/parser/branch.cton index 4adb4b5d27..283dd9a03a 100644 --- a/filetests/parser/branch.cton +++ b/filetests/parser/branch.cton @@ -9,7 +9,7 @@ ebb0: ebb1: jump ebb0() } -; sameln: function %minimal() { +; sameln: function %minimal() native { ; nextln: ebb0: ; nextln: jump ebb1 ; nextln: @@ -25,7 +25,7 @@ ebb0(v90: i32): ebb1(v91: i32): jump ebb0(v91) } -; sameln: function %onearg(i32) { +; sameln: function %onearg(i32) native { ; nextln: ebb0($v90: i32): ; nextln: jump ebb1($v90) ; nextln: @@ -41,7 +41,7 @@ ebb0(v90: i32, v91: f32): ebb1(v92: i32, v93: f32): jump ebb0(v92, v93) } -; sameln: function %twoargs(i32, f32) { +; sameln: function %twoargs(i32, f32) native { ; nextln: ebb0($v90: i32, $v91: f32): ; nextln: jump ebb1($v90, $v91) ; nextln: @@ -57,7 +57,7 @@ ebb0(v90: i32): ebb1: brnz v90, ebb1() } -; sameln: function %minimal(i32) { +; sameln: function %minimal(i32) native { ; nextln: ebb0($v90: i32): ; nextln: brz $v90, ebb1 ; nextln: @@ -72,7 +72,7 @@ ebb0(v90: i32, v91: f32): ebb1(v92: i32, v93: f32): brnz v90, ebb0(v92, v93) } -; sameln: function %twoargs(i32, f32) { +; sameln: function %twoargs(i32, f32) native { ; nextln: ebb0($v90: i32, $v91: f32): ; nextln: brz $v90, ebb1($v90, $v91) ; nextln: @@ -94,7 +94,7 @@ ebb30: ebb40: trap } -; sameln: function %jumptable(i32) { +; sameln: function %jumptable(i32) native { ; nextln: jt0 = jump_table 0 ; nextln: jt1 = jump_table 0, 0, ebb0, ebb3, ebb1, ebb2 ; nextln: diff --git a/filetests/parser/call.cton b/filetests/parser/call.cton index 9d7c2c3d16..662925db7f 100644 --- a/filetests/parser/call.cton +++ b/filetests/parser/call.cton @@ -5,18 +5,18 @@ function %mini() { ebb1: return } -; sameln: function %mini() { +; sameln: function %mini() native { ; nextln: ebb0: ; nextln: return ; nextln: } -function %r1() -> i32, f32 { +function %r1() -> i32, f32 spiderwasm { ebb1: v1 = iconst.i32 3 v2 = f32const 0.0 return v1, v2 } -; sameln: function %r1() -> i32, f32 { +; sameln: function %r1() -> i32, f32 spiderwasm { ; nextln: ebb0: ; nextln: $v1 = iconst.i32 3 ; nextln: $v2 = f32const 0.0 @@ -24,15 +24,15 @@ ebb1: ; nextln: } function %signatures() { - sig10 = signature() - sig11 = signature(i32, f64) -> i32, b1 + sig10 = () + sig11 = (i32, f64) -> i32, b1 spiderwasm fn5 = sig11 %foo fn8 = function %bar(i32) -> b1 } -; sameln: function %signatures() { -; nextln: $sig10 = signature() -; nextln: $sig11 = signature(i32, f64) -> i32, b1 -; nextln: sig2 = signature(i32) -> b1 +; sameln: function %signatures() native { +; nextln: $sig10 = () native +; nextln: $sig11 = (i32, f64) -> i32, b1 spiderwasm +; nextln: sig2 = (i32) -> b1 native ; nextln: $fn5 = $sig11 %foo ; nextln: $fn8 = sig2 %bar ; nextln: } @@ -54,9 +54,9 @@ ebb0: ; check: return function %indirect(i64) { - sig0 = signature(i64) - sig1 = signature() -> i32 - sig2 = signature() -> i32, f32 + sig0 = (i64) + sig1 = () -> i32 + sig2 = () -> i32, f32 ebb0(v0: i64): v1 = call_indirect sig1, v0() @@ -74,7 +74,7 @@ function %special1(i32 sret, i32 fp, i32 csr, i32 link) -> i32 link, i32 fp, i32 ebb0(v1: i32, v2: i32, v3: i32, v4: i32): return v4, v2, v3, v1 } -; check: function %special1(i32 sret, i32 fp, i32 csr, i32 link) -> i32 link, i32 fp, i32 csr, i32 sret { +; check: function %special1(i32 sret, i32 fp, i32 csr, i32 link) -> i32 link, i32 fp, i32 csr, i32 sret native { ; check: ebb0($v1: i32, $v2: i32, $v3: i32, $v4: i32): ; check: return $v4, $v2, $v3, $v1 ; check: } diff --git a/filetests/parser/instruction_encoding.cton b/filetests/parser/instruction_encoding.cton index bc2e1dd239..c808892701 100644 --- a/filetests/parser/instruction_encoding.cton +++ b/filetests/parser/instruction_encoding.cton @@ -13,7 +13,7 @@ ebb1(v0: i32, v1: i32): v9 = iadd v8, v7 [Iret#5] return v0, v8 } -; sameln: function %foo(i32, i32) { +; sameln: function %foo(i32, i32) native { ; nextln: $ebb1($v0: i32, $v1: i32): ; nextln: [-,-]$WS $v2 = iadd $v0, $v1 ; nextln: [-]$WS trap diff --git a/filetests/parser/keywords.cton b/filetests/parser/keywords.cton index 37d0390a58..a4b894574e 100644 --- a/filetests/parser/keywords.cton +++ b/filetests/parser/keywords.cton @@ -2,4 +2,4 @@ test cat ; 'function' is not a keyword, and can be used as the name of a function too. function %function() {} -; check: function %function() +; check: function %function() native diff --git a/filetests/parser/rewrite.cton b/filetests/parser/rewrite.cton index f7ebfa3876..c6a04a9e9c 100644 --- a/filetests/parser/rewrite.cton +++ b/filetests/parser/rewrite.cton @@ -15,7 +15,7 @@ ebb100(v20: i32): v9200 = f64const 0x4.0p0 trap } -; sameln: function %defs() { +; sameln: function %defs() native { ; nextln: $ebb100($v20: i32): ; nextln: $v1000 = iconst.i32x8 5 ; nextln: $v9200 = f64const 0x1.0000000000000p2 @@ -29,7 +29,7 @@ ebb100(v20: i32): v200 = iadd v20, v1000 jump ebb100(v1000) } -; sameln: function %use_value() { +; sameln: function %use_value() native { ; nextln: ebb0($v20: i32): ; nextln: $v1000 = iadd_imm $v20, 5 ; nextln: $v200 = iadd $v20, $v1000 diff --git a/filetests/parser/tiny.cton b/filetests/parser/tiny.cton index fff7ccd4c1..ecd2525ba2 100644 --- a/filetests/parser/tiny.cton +++ b/filetests/parser/tiny.cton @@ -5,7 +5,7 @@ function %minimal() { ebb0: trap } -; sameln: function %minimal() { +; sameln: function %minimal() native { ; nextln: ebb0: ; nextln: trap ; nextln: } @@ -18,7 +18,7 @@ ebb0: v1 = iconst.i8 6 v2 = ishl v0, v1 } -; sameln: function %ivalues() { +; sameln: function %ivalues() native { ; nextln: ebb0: ; nextln: $v0 = iconst.i32 2 ; nextln: $v1 = iconst.i8 6 @@ -34,7 +34,7 @@ ebb0: v2 = bextend.b32 v1 v3 = bxor v0, v2 } -; sameln: function %bvalues() { +; sameln: function %bvalues() native { ; nextln: ebb0: ; nextln: $v0 = bconst.b32 true ; nextln: $v1 = bconst.b8 false @@ -47,7 +47,7 @@ function %select() { ebb0(v90: i32, v91: i32, v92: b1): v0 = select v92, v90, v91 } -; sameln: function %select() { +; sameln: function %select() native { ; nextln: ebb0($v90: i32, $v91: i32, $v92: b1): ; nextln: $v0 = select $v92, $v90, $v91 ; nextln: } @@ -59,7 +59,7 @@ ebb0: v1 = extractlane v0, 3 v2 = insertlane v0, 1, v1 } -; sameln: function %lanes() { +; sameln: function %lanes() native { ; nextln: ebb0: ; nextln: $v0 = iconst.i32x4 2 ; nextln: $v1 = extractlane $v0, 3 @@ -75,7 +75,7 @@ ebb0(v90: i32, v91: i32): v3 = irsub_imm v91, 45 br_icmp eq v90, v91, ebb0(v91, v90) } -; sameln: function %icmp(i32, i32) { +; sameln: function %icmp(i32, i32) native { ; nextln: ebb0($v90: i32, $v91: i32): ; nextln: $v0 = icmp eq $v90, $v91 ; nextln: $v1 = icmp ult $v90, $v91 @@ -91,7 +91,7 @@ ebb0(v90: f32, v91: f32): v1 = fcmp uno v90, v91 v2 = fcmp lt v90, v91 } -; sameln: function %fcmp(f32, f32) { +; sameln: function %fcmp(f32, f32) native { ; nextln: ebb0($v90: f32, $v91: f32): ; nextln: $v0 = fcmp eq $v90, $v91 ; nextln: $v1 = fcmp uno $v90, $v91 @@ -105,7 +105,7 @@ ebb0(v90: i32, v91: f32): v0 = bitcast.i8x4 v90 v1 = bitcast.i32 v91 } -; sameln: function %bitcast(i32, f32) { +; sameln: function %bitcast(i32, f32) native { ; nextln: ebb0($v90: i32, $v91: f32): ; nextln: $v0 = bitcast.i8x4 $v90 ; nextln: $v1 = bitcast.i32 $v91 @@ -124,7 +124,7 @@ ebb0: stack_store v1, ss10+2 stack_store v2, ss2 } -; sameln: function %stack() { +; sameln: function %stack() native { ; nextln: $ss10 = spill_slot 8 ; nextln: $ss2 = local 4 ; nextln: $ss3 = incoming_arg 4, offset 8 @@ -144,7 +144,7 @@ ebb0(v1: i32): v3 = heap_load.f32 v1+12 heap_store v3, v1 } -; sameln: function %heap(i32) { +; sameln: function %heap(i32) native { ; nextln: ebb0($v1: i32): ; nextln: $v2 = heap_load.f32 $v1 ; nextln: $v3 = heap_load.f32 $v1+12 @@ -164,7 +164,7 @@ ebb0(v1: i32): store aligned v3, v1+12 store notrap aligned v3, v1-12 } -; sameln: function %memory(i32) { +; sameln: function %memory(i32) native { ; nextln: ebb0($v1: i32): ; nextln: $v2 = load.i64 $v1 ; nextln: $v3 = load.i64 aligned $v1 @@ -185,7 +185,7 @@ ebb0(v1: i32): regmove v1, %20 -> %10 return } -; sameln: function %diversion(i32) { +; sameln: function %diversion(i32) native { ; nextln: ebb0($v1: i32): ; nextln: regmove $v1, %10 -> %20 ; nextln: regmove $v1, %20 -> %10 diff --git a/filetests/regalloc/spill.cton b/filetests/regalloc/spill.cton index 4c470a73e6..901509a8d4 100644 --- a/filetests/regalloc/spill.cton +++ b/filetests/regalloc/spill.cton @@ -93,7 +93,7 @@ ebb0(v0: i32): ; The same value used as indirect callee and argument. function %doubleuse_icall1(i32) { - sig0 = signature(i32) + sig0 = (i32) native ebb0(v0: i32): ; not:copy call_indirect sig0, v0(v0) @@ -102,7 +102,7 @@ ebb0(v0: i32): ; The same value used as indirect callee and two arguments. function %doubleuse_icall2(i32) { - sig0 = signature(i32, i32) + sig0 = (i32, i32) native ebb0(v0: i32): ; check: $(c=$V) = copy $v0 call_indirect sig0, v0(v0, v0) diff --git a/lib/cretonne/src/ir/extfunc.rs b/lib/cretonne/src/ir/extfunc.rs index 4cf5b9d61f..16211a4d83 100644 --- a/lib/cretonne/src/ir/extfunc.rs +++ b/lib/cretonne/src/ir/extfunc.rs @@ -25,6 +25,9 @@ pub struct Signature { /// Types returned from the function. pub return_types: Vec, + /// Calling convention. + pub call_conv: CallConv, + /// When the signature has been legalized to a specific ISA, this holds the size of the /// argument array on the stack. Before legalization, this is `None`. /// @@ -35,10 +38,11 @@ pub struct Signature { impl Signature { /// Create a new blank signature. - pub fn new() -> Signature { + pub fn new(call_conv: CallConv) -> Signature { Signature { argument_types: Vec::new(), return_types: Vec::new(), + call_conv, argument_bytes: None, } } @@ -94,7 +98,7 @@ impl<'a> fmt::Display for DisplaySignature<'a> { write!(f, " -> ")?; write_list(f, &self.0.return_types, self.1)?; } - Ok(()) + write!(f, " {}", self.0.call_conv) } } @@ -278,6 +282,46 @@ impl fmt::Display for ExtFuncData { } } +/// A Calling convention. +/// +/// A function's calling convention determines exactly how arguments and return values are passed, +/// and how stack frames are managed. Since all of these details depend on both the instruction set +/// architecture and possibly the operating system, a function's calling convention is only fully +/// determined by a `(TargetIsa, CallConv)` tuple. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CallConv { + /// The C calling convention. + /// + /// This is the native calling convention that a C compiler would use on the platform. + Native, + + /// A JIT-compiled WebAssembly function in the SpiderMonkey VM. + SpiderWASM, +} + +impl fmt::Display for CallConv { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::CallConv::*; + f.write_str(match *self { + Native => "native", + SpiderWASM => "spiderwasm", + }) + } +} + +impl FromStr for CallConv { + type Err = (); + + fn from_str(s: &str) -> Result { + use self::CallConv::*; + match s { + "native" => Ok(Native), + "spiderwasm" => Ok(SpiderWASM), + _ => Err(()), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -306,19 +350,26 @@ mod tests { } } + #[test] + fn call_conv() { + for &cc in &[CallConv::Native, CallConv::SpiderWASM] { + assert_eq!(Ok(cc), cc.to_string().parse()) + } + } + #[test] fn signatures() { - let mut sig = Signature::new(); - assert_eq!(sig.to_string(), "()"); + let mut sig = Signature::new(CallConv::SpiderWASM); + assert_eq!(sig.to_string(), "() spiderwasm"); sig.argument_types.push(ArgumentType::new(I32)); - assert_eq!(sig.to_string(), "(i32)"); + assert_eq!(sig.to_string(), "(i32) spiderwasm"); sig.return_types.push(ArgumentType::new(F32)); - assert_eq!(sig.to_string(), "(i32) -> f32"); + assert_eq!(sig.to_string(), "(i32) -> f32 spiderwasm"); sig.argument_types .push(ArgumentType::new(I32.by(4).unwrap())); - assert_eq!(sig.to_string(), "(i32, i32x4) -> f32"); + assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 spiderwasm"); sig.return_types.push(ArgumentType::new(B8)); - assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8"); + assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8 spiderwasm"); // Test the offset computation algorithm. assert_eq!(sig.argument_bytes, None); @@ -332,6 +383,7 @@ mod tests { assert_eq!(sig.argument_bytes, Some(28)); // Writing ABI-annotated signatures. - assert_eq!(sig.to_string(), "(i32 [24], i32x4 [8]) -> f32, b8"); + assert_eq!(sig.to_string(), + "(i32 [24], i32x4 [8]) -> f32, b8 spiderwasm"); } } diff --git a/lib/cretonne/src/ir/function.rs b/lib/cretonne/src/ir/function.rs index 59213aff13..c2b280dc6c 100644 --- a/lib/cretonne/src/ir/function.rs +++ b/lib/cretonne/src/ir/function.rs @@ -4,7 +4,7 @@ //! instructions. use entity_map::{EntityMap, PrimaryEntityData}; -use ir::{FunctionName, Signature, JumpTableData, DataFlowGraph, Layout}; +use ir::{FunctionName, CallConv, Signature, JumpTableData, DataFlowGraph, Layout}; use ir::{JumpTables, InstEncodings, ValueLocations, StackSlots, EbbOffsets}; use isa::TargetIsa; use std::fmt; @@ -67,9 +67,9 @@ impl Function { } } - /// Create a new empty, anonymous function. + /// Create a new empty, anonymous function with a native calling convention. pub fn new() -> Function { - Self::with_name_signature(FunctionName::default(), Signature::new()) + Self::with_name_signature(FunctionName::default(), Signature::new(CallConv::Native)) } /// Return an object that can display this function with correct ISA-specific annotations. diff --git a/lib/cretonne/src/ir/mod.rs b/lib/cretonne/src/ir/mod.rs index ebe39a3fcb..76f37efa3c 100644 --- a/lib/cretonne/src/ir/mod.rs +++ b/lib/cretonne/src/ir/mod.rs @@ -18,7 +18,8 @@ mod progpoint; mod valueloc; pub use ir::funcname::FunctionName; -pub use ir::extfunc::{Signature, ArgumentType, ArgumentExtension, ArgumentPurpose, ExtFuncData}; +pub use ir::extfunc::{Signature, CallConv, ArgumentType, ArgumentExtension, ArgumentPurpose, + ExtFuncData}; pub use ir::types::Type; pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef}; pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool}; diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index 56a06a0c04..c342c4f273 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -54,7 +54,7 @@ fn write_preamble(w: &mut Write, for sig in func.dfg.signatures.keys() { any = true; writeln!(w, - " {} = signature{}", + " {} = {}", sig, func.dfg.signatures[sig].display(regs))?; } @@ -366,26 +366,27 @@ mod tests { #[test] fn basic() { let mut f = Function::new(); - assert_eq!(f.to_string(), "function %() {\n}\n"); + assert_eq!(f.to_string(), "function %() native {\n}\n"); f.name = FunctionName::new("foo"); - assert_eq!(f.to_string(), "function %foo() {\n}\n"); + assert_eq!(f.to_string(), "function %foo() native {\n}\n"); f.stack_slots .push(StackSlotData::new(StackSlotKind::Local, 4)); - assert_eq!(f.to_string(), "function %foo() {\n ss0 = local 4\n}\n"); + assert_eq!(f.to_string(), + "function %foo() native {\n ss0 = local 4\n}\n"); let ebb = f.dfg.make_ebb(); f.layout.append_ebb(ebb); assert_eq!(f.to_string(), - "function %foo() {\n ss0 = local 4\n\nebb0:\n}\n"); + "function %foo() native {\n ss0 = local 4\n\nebb0:\n}\n"); f.dfg.append_ebb_arg(ebb, types::I8); assert_eq!(f.to_string(), - "function %foo() {\n ss0 = local 4\n\nebb0(v0: i8):\n}\n"); + "function %foo() native {\n ss0 = local 4\n\nebb0(v0: i8):\n}\n"); f.dfg.append_ebb_arg(ebb, types::F32.by(4).unwrap()); assert_eq!(f.to_string(), - "function %foo() {\n ss0 = local 4\n\nebb0(v0: i8, v1: f32x4):\n}\n"); + "function %foo() native {\n ss0 = local 4\n\nebb0(v0: i8, v1: f32x4):\n}\n"); } } diff --git a/lib/frontend/src/frontend.rs b/lib/frontend/src/frontend.rs index 7f9fb9edb6..d81cee4bc7 100644 --- a/lib/frontend/src/frontend.rs +++ b/lib/frontend/src/frontend.rs @@ -572,7 +572,7 @@ impl<'a, Variable> FunctionBuilder<'a, Variable> mod tests { use cretonne::entity_ref::EntityRef; - use cretonne::ir::{FunctionName, Function, Signature, ArgumentType, InstBuilder}; + use cretonne::ir::{FunctionName, Function, CallConv, Signature, ArgumentType, InstBuilder}; use cretonne::ir::types::*; use frontend::{ILBuilder, FunctionBuilder}; use cretonne::verifier::verify_function; @@ -600,7 +600,7 @@ mod tests { #[test] fn sample_function() { - let mut sig = Signature::new(); + let mut sig = Signature::new(CallConv::Native); sig.return_types.push(ArgumentType::new(I32)); sig.argument_types.push(ArgumentType::new(I32)); diff --git a/lib/frontend/src/lib.rs b/lib/frontend/src/lib.rs index 2cbbc30459..10a06b23cc 100644 --- a/lib/frontend/src/lib.rs +++ b/lib/frontend/src/lib.rs @@ -36,7 +36,7 @@ //! extern crate cton_frontend; //! //! use cretonne::entity_ref::EntityRef; -//! use cretonne::ir::{FunctionName, Function, Signature, ArgumentType, InstBuilder}; +//! use cretonne::ir::{FunctionName, CallConv, Function, Signature, ArgumentType, InstBuilder}; //! use cretonne::ir::types::*; //! use cton_frontend::{ILBuilder, FunctionBuilder}; //! use cretonne::verifier::verify_function; @@ -62,7 +62,7 @@ //! } //! //! fn main() { -//! let mut sig = Signature::new(); +//! let mut sig = Signature::new(CallConv::Native); //! sig.return_types.push(ArgumentType::new(I32)); //! sig.argument_types.push(ArgumentType::new(I32)); //! let mut il_builder = ILBuilder::::new(); diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 865cdcbd0d..fe2aec3e38 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -9,9 +9,9 @@ use std::collections::HashMap; use std::str::FromStr; use std::{u16, u32}; use std::mem; -use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, StackSlotData, JumpTable, - JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef, - FuncRef, StackSlot, ValueLoc, ArgumentLoc, MemFlags}; +use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, CallConv, StackSlotData, + JumpTable, JumpTableData, Signature, ArgumentType, ArgumentExtension, + ExtFuncData, SigRef, FuncRef, StackSlot, ValueLoc, ArgumentLoc, MemFlags}; use cretonne::ir::types::VOID; use cretonne::ir::immediates::{Imm64, Offset32, Uoffset32, Ieee32, Ieee64}; use cretonne::ir::entities::AnyEntity; @@ -767,13 +767,14 @@ impl<'a> Parser<'a> { // Parse a function signature. // - // signature ::= * "(" [arglist] ")" ["->" retlist] [call_conv] + // signature ::= * "(" [arglist] ")" ["->" retlist] [callconv] // fn parse_signature(&mut self, unique_isa: Option<&TargetIsa>) -> Result { - let mut sig = Signature::new(); + // Calling convention defaults to `native`, but can be changed. + let mut sig = Signature::new(CallConv::Native); self.match_token(Token::LPar, "expected function signature: ( args... )")?; - // signature ::= "(" * [arglist] ")" ["->" retlist] [call_conv] + // signature ::= "(" * [arglist] ")" ["->" retlist] [callconv] if self.token() != Some(Token::RPar) { sig.argument_types = self.parse_argument_list(unique_isa)?; } @@ -782,12 +783,21 @@ impl<'a> Parser<'a> { sig.return_types = self.parse_argument_list(unique_isa)?; } + // The calling convention is optional. + if let Some(Token::Identifier(text)) = self.token() { + match text.parse() { + Ok(cc) => { + self.consume(); + sig.call_conv = cc; + } + _ => return err!(self.loc, "unknown calling convention: {}", text), + } + } + if sig.argument_types.iter().all(|a| a.location.is_assigned()) { sig.compute_argument_bytes(); } - // TBD: calling convention. - Ok(sig) } @@ -951,12 +961,11 @@ impl<'a> Parser<'a> { // Parse a signature decl. // - // signature-decl ::= SigRef(sigref) "=" "signature" signature + // signature-decl ::= SigRef(sigref) "=" signature // fn parse_signature_decl(&mut self, unique_isa: Option<&TargetIsa>) -> Result<(u32, Signature)> { let number = self.match_sig("expected signature number: sig«n»")?; self.match_token(Token::Equal, "expected '=' in signature decl")?; - self.match_identifier("signature", "expected 'signature'")?; let data = self.parse_signature(unique_isa)?; Ok((number, data)) } @@ -1755,7 +1764,7 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use super::*; - use cretonne::ir::{ArgumentExtension, ArgumentPurpose}; + use cretonne::ir::{CallConv, ArgumentExtension, ArgumentPurpose}; use cretonne::ir::types; use cretonne::ir::StackSlotKind; use cretonne::ir::entities::AnyEntity; @@ -1777,7 +1786,7 @@ mod tests { #[test] fn aliases() { - let (func, details) = Parser::new("function %qux() { + let (func, details) = Parser::new("function %qux() native { ebb0: v4 = iconst.i8 6 v3 -> v4 @@ -1801,15 +1810,26 @@ mod tests { #[test] fn signature() { - let sig = Parser::new("()").parse_signature(None).unwrap(); + let sig = Parser::new("()native").parse_signature(None).unwrap(); assert_eq!(sig.argument_types.len(), 0); assert_eq!(sig.return_types.len(), 0); + assert_eq!(sig.call_conv, CallConv::Native); - let sig2 = Parser::new("(i8 uext, f32, f64, i32 sret) -> i32 sext, f64") + let sig2 = Parser::new("(i8 uext, f32, f64, i32 sret) -> i32 sext, f64 spiderwasm") .parse_signature(None) .unwrap(); assert_eq!(sig2.to_string(), - "(i8 uext, f32, f64, i32 sret) -> i32 sext, f64"); + "(i8 uext, f32, f64, i32 sret) -> i32 sext, f64 spiderwasm"); + assert_eq!(sig2.call_conv, CallConv::SpiderWASM); + + // Old-style signature without a calling convention. + assert_eq!(Parser::new("()").parse_signature(None).unwrap().to_string(), + "() native"); + assert_eq!(Parser::new("() notacc") + .parse_signature(None) + .unwrap_err() + .to_string(), + "1: unknown calling convention: notacc"); // `void` is not recognized as a type by the lexer. It should not appear in files. assert_eq!(Parser::new("() -> void") @@ -1831,7 +1851,7 @@ mod tests { #[test] fn stack_slot_decl() { - let (func, _) = Parser::new("function %foo() { + let (func, _) = Parser::new("function %foo() native { ss3 = incoming_arg 13 ss1 = spill_slot 1 }") @@ -1850,7 +1870,7 @@ mod tests { assert_eq!(iter.next(), None); // Catch duplicate definitions. - assert_eq!(Parser::new("function %bar() { + assert_eq!(Parser::new("function %bar() native { ss1 = spill_slot 13 ss1 = spill_slot 1 }") @@ -1862,7 +1882,7 @@ mod tests { #[test] fn ebb_header() { - let (func, _) = Parser::new("function %ebbs() { + let (func, _) = Parser::new("function %ebbs() native { ebb0: ebb4(v3: i32): }") @@ -1884,7 +1904,7 @@ mod tests { #[test] fn comments() { let (func, Details { comments, .. }) = Parser::new("; before - function %comment() { ; decl + function %comment() native { ; decl ss10 = outgoing_arg 13 ; stackslot. ; Still stackslot. jt10 = jump_table ebb0 @@ -1924,7 +1944,7 @@ mod tests { test verify set enable_float=false ; still preamble - function %comment() {}") + function %comment() native {}") .unwrap(); assert_eq!(tf.commands.len(), 2); assert_eq!(tf.commands[0].command, "cfg"); @@ -1947,17 +1967,17 @@ mod tests { #[cfg(build_riscv)] fn isa_spec() { assert!(parse_test("isa - function %foo() {}") + function %foo() native {}") .is_err()); assert!(parse_test("isa riscv set enable_float=false - function %foo() {}") + function %foo() native {}") .is_err()); match parse_test("set enable_float=false isa riscv - function %foo() {}") + function %foo() native {}") .unwrap() .isa_spec { IsaSpec::None(_) => panic!("Expected some ISA"), @@ -1971,7 +1991,7 @@ mod tests { #[test] fn binary_function_name() { // Valid characters in the name. - let func = Parser::new("function #1234567890AbCdEf() { + let func = Parser::new("function #1234567890AbCdEf() native { ebb0: trap }") @@ -1981,21 +2001,21 @@ mod tests { assert_eq!(func.name.to_string(), "#1234567890abcdef"); // Invalid characters in the name. - let mut parser = Parser::new("function #12ww() { + let mut parser = Parser::new("function #12ww() native { ebb0: trap }"); assert!(parser.parse_function(None).is_err()); // The length of binary function name should be multiple of two. - let mut parser = Parser::new("function #1() { + let mut parser = Parser::new("function #1() native { ebb0: trap }"); assert!(parser.parse_function(None).is_err()); // Empty binary function name should be valid. - let func = Parser::new("function #() { + let func = Parser::new("function #() native { ebb0: trap }") diff --git a/tests/cfg_traversal.rs b/tests/cfg_traversal.rs index a0c9a64d90..37d568e0e4 100644 --- a/tests/cfg_traversal.rs +++ b/tests/cfg_traversal.rs @@ -27,7 +27,7 @@ fn test_reverse_postorder_traversal(function_source: &str, ebb_order: Vec) #[test] fn simple_traversal() { test_reverse_postorder_traversal(" - function %test(i32) { + function %test(i32) native { ebb0(v0: i32): brz v0, ebb1 jump ebb2 @@ -56,7 +56,7 @@ fn simple_traversal() { #[test] fn loops_one() { test_reverse_postorder_traversal(" - function %test(i32) { + function %test(i32) native { ebb0(v0: i32): jump ebb1 ebb1: @@ -74,7 +74,7 @@ fn loops_one() { #[test] fn loops_two() { test_reverse_postorder_traversal(" - function %test(i32) { + function %test(i32) native { ebb0(v0: i32): brz v0, ebb1 jump ebb2 @@ -99,7 +99,7 @@ fn loops_two() { #[test] fn loops_three() { test_reverse_postorder_traversal(" - function %test(i32) { + function %test(i32) native { ebb0(v0: i32): brz v0, ebb1 jump ebb2 @@ -129,7 +129,7 @@ fn loops_three() { #[test] fn back_edge_one() { test_reverse_postorder_traversal(" - function %test(i32) { + function %test(i32) native { ebb0(v0: i32): brz v0, ebb1 jump ebb2