Add a "colocated" flag to symbol references. (#298)
This adds a "colocated" flag to function and symbolic global variables which indicates that they are defined along with the current function, so they can use PC-relative addressing. This also changes the function decl syntax; the name now always precedes the signature, and the "function" keyword is no longer included.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
test verifier
|
test verifier
|
||||||
|
|
||||||
function %gcd(i32 uext, i32 uext) -> i32 uext system_v {
|
function %gcd(i32 uext, i32 uext) -> i32 uext system_v {
|
||||||
fn1 = function %divmod(i32 uext, i32 uext) -> i32 uext, i32 uext
|
fn1 = %divmod(i32 uext, i32 uext) -> i32 uext, i32 uext
|
||||||
|
|
||||||
ebb1(v1: i32, v2: i32):
|
ebb1(v1: i32, v2: i32):
|
||||||
brz v2, ebb2
|
brz v2, ebb2
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ A ``.cton`` file consists of a sequence of independent function definitions:
|
|||||||
|
|
||||||
.. productionlist::
|
.. productionlist::
|
||||||
function_list : { function }
|
function_list : { function }
|
||||||
function : function_spec "{" preamble function_body "}"
|
function : "function" function_name signature "{" preamble function_body "}"
|
||||||
function_spec : "function" function_name signature
|
|
||||||
preamble : { preamble_decl }
|
preamble : { preamble_decl }
|
||||||
function_body : { extended_basic_block }
|
function_body : { extended_basic_block }
|
||||||
|
|
||||||
@@ -409,10 +408,14 @@ compilers.
|
|||||||
Functions that are called directly must be declared in the :term:`function
|
Functions that are called directly must be declared in the :term:`function
|
||||||
preamble`:
|
preamble`:
|
||||||
|
|
||||||
.. inst:: FN = function NAME signature
|
.. inst:: FN = [colocated] NAME signature
|
||||||
|
|
||||||
Declare a function so it can be called directly.
|
Declare a function so it can be called directly.
|
||||||
|
|
||||||
|
If the colocated keyword is present, the symbol's definition will be
|
||||||
|
defined along with the current function, such that it can use more
|
||||||
|
efficient addressing.
|
||||||
|
|
||||||
:arg NAME: Name of the function, passed to the linker for resolution.
|
:arg NAME: Name of the function, passed to the linker for resolution.
|
||||||
:arg signature: Function signature. See below.
|
:arg signature: Function signature. See below.
|
||||||
:result FN: A function identifier that can be used with :inst:`call`.
|
:result FN: A function identifier that can be used with :inst:`call`.
|
||||||
@@ -570,13 +573,17 @@ runtime data structures.
|
|||||||
variable.
|
variable.
|
||||||
:result GV: Global variable.
|
:result GV: Global variable.
|
||||||
|
|
||||||
.. inst:: GV = globalsym name
|
.. inst:: GV = [colocated] globalsym name
|
||||||
|
|
||||||
Declare a global variable at a symbolic address.
|
Declare a global variable at a symbolic address.
|
||||||
|
|
||||||
The address of GV is symbolic and will be assigned a relocation, so that
|
The address of GV is symbolic and will be assigned a relocation, so that
|
||||||
it can be resolved by a later linking phase.
|
it can be resolved by a later linking phase.
|
||||||
|
|
||||||
|
If the colocated keyword is present, the symbol's definition will be
|
||||||
|
defined along with the current function, such that it can use more
|
||||||
|
efficient addressing.
|
||||||
|
|
||||||
:arg name: External name.
|
:arg name: External name.
|
||||||
:result GV: Global variable.
|
:result GV: Global variable.
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ isa riscv
|
|||||||
|
|
||||||
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
||||||
sig0 = ()
|
sig0 = ()
|
||||||
fn0 = function %foo()
|
fn0 = %foo()
|
||||||
|
|
||||||
ebb0(v9999: i32):
|
ebb0(v9999: i32):
|
||||||
[-,%x10] v1 = iconst.i32 1
|
[-,%x10] v1 = iconst.i32 1
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ ebb0(v0: i64):
|
|||||||
}
|
}
|
||||||
|
|
||||||
function %split_call_arg(i32) {
|
function %split_call_arg(i32) {
|
||||||
fn1 = function %foo(i64)
|
fn1 = %foo(i64)
|
||||||
fn2 = function %foo(i32, i64)
|
fn2 = %foo(i32, i64)
|
||||||
ebb0(v0: i32):
|
ebb0(v0: i32):
|
||||||
v1 = uextend.i64 v0
|
v1 = uextend.i64 v0
|
||||||
call fn1(v1)
|
call fn1(v1)
|
||||||
@@ -30,7 +30,7 @@ ebb0(v0: i32):
|
|||||||
}
|
}
|
||||||
|
|
||||||
function %split_ret_val() {
|
function %split_ret_val() {
|
||||||
fn1 = function %foo() -> i64
|
fn1 = %foo() -> i64
|
||||||
ebb0:
|
ebb0:
|
||||||
v1 = call fn1()
|
v1 = call fn1()
|
||||||
; check: ebb0($(link=$V): i32):
|
; check: ebb0($(link=$V): i32):
|
||||||
@@ -45,7 +45,7 @@ ebb1(v10: i64):
|
|||||||
|
|
||||||
; First return value is fine, second one is expanded.
|
; First return value is fine, second one is expanded.
|
||||||
function %split_ret_val2() {
|
function %split_ret_val2() {
|
||||||
fn1 = function %foo() -> i32, i64
|
fn1 = %foo() -> i32, i64
|
||||||
ebb0:
|
ebb0:
|
||||||
v1, v2 = call fn1()
|
v1, v2 = call fn1()
|
||||||
; check: ebb0($(link=$V): i32):
|
; check: ebb0($(link=$V): i32):
|
||||||
@@ -70,7 +70,7 @@ ebb0(v1: i8, v2: i8, v3: i8):
|
|||||||
|
|
||||||
; Function produces single return value, still need to copy.
|
; Function produces single return value, still need to copy.
|
||||||
function %ext_ret_val() {
|
function %ext_ret_val() {
|
||||||
fn1 = function %foo() -> i8 sext
|
fn1 = %foo() -> i8 sext
|
||||||
ebb0:
|
ebb0:
|
||||||
v1 = call fn1()
|
v1 = call fn1()
|
||||||
; check: ebb0($V: i32):
|
; check: ebb0($V: i32):
|
||||||
@@ -124,7 +124,7 @@ ebb0(v0: i32, v1: f32x2):
|
|||||||
; Call a function that takes arguments on the stack.
|
; Call a function that takes arguments on the stack.
|
||||||
function %stack_args(i32) {
|
function %stack_args(i32) {
|
||||||
; check: $(ss0=$SS) = outgoing_arg 4
|
; check: $(ss0=$SS) = outgoing_arg 4
|
||||||
fn1 = function %foo(i64, i64, i64, i64, i32)
|
fn1 = %foo(i64, i64, i64, i64, i32)
|
||||||
ebb0(v0: i32):
|
ebb0(v0: i32):
|
||||||
v1 = iconst.i64 1
|
v1 = iconst.i64 1
|
||||||
call fn1(v1, v1, v1, v1, v0)
|
call fn1(v1, v1, v1, v1, v0)
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ function %parse_encoding(i32 [%x5]) -> i32 [%x10] {
|
|||||||
; check: sig5 = () -> f32 [0] system_v
|
; check: sig5 = () -> f32 [0] system_v
|
||||||
|
|
||||||
; function + signature
|
; function + signature
|
||||||
fn0 = function %bar(i32 [%x10]) -> b1 [%x10] system_v
|
fn0 = %bar(i32 [%x10]) -> b1 [%x10] system_v
|
||||||
; check: sig6 = (i32 [%x10]) -> b1 [%x10] system_v
|
; check: sig6 = (i32 [%x10]) -> b1 [%x10] system_v
|
||||||
; nextln: fn0 = sig6 %bar
|
; nextln: fn0 = %bar sig6
|
||||||
|
|
||||||
ebb0(v0: i32):
|
ebb0(v0: i32):
|
||||||
return v0
|
return v0
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ test verifier
|
|||||||
isa riscv
|
isa riscv
|
||||||
|
|
||||||
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
||||||
fn0 = function %foo()
|
fn0 = %foo()
|
||||||
|
|
||||||
ebb0(v9999: i32):
|
ebb0(v9999: i32):
|
||||||
; iconst.i32 needs legalizing, so it should throw a
|
; iconst.i32 needs legalizing, so it should throw a
|
||||||
@@ -11,7 +11,7 @@ ebb0(v9999: i32):
|
|||||||
}
|
}
|
||||||
|
|
||||||
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
||||||
fn0 = function %foo()
|
fn0 = %foo()
|
||||||
|
|
||||||
ebb0(v9999: i32):
|
ebb0(v9999: i32):
|
||||||
v1 = iconst.i32 1
|
v1 = iconst.i32 1
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ ebb0:
|
|||||||
|
|
||||||
function %pass_stack_int64(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 vmctx) spiderwasm {
|
function %pass_stack_int64(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 vmctx) spiderwasm {
|
||||||
sig0 = (i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 vmctx) spiderwasm
|
sig0 = (i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 vmctx) spiderwasm
|
||||||
fn0 = sig0 u0:0
|
fn0 = u0:0 sig0
|
||||||
|
|
||||||
ebb0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v8: i64, v9: i64, v10: i64, v11: i64, v12: i64, v13: i64, v14: i64, v15: i64, v16: i64, v17: i64, v18: i64, v19: i64, v20: i64):
|
ebb0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v8: i64, v9: i64, v10: i64, v11: i64, v12: i64, v13: i64, v14: i64, v15: i64, v16: i64, v17: i64, v18: i64, v19: i64, v20: i64):
|
||||||
call fn0(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
|
call fn0(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ isa x86 haswell
|
|||||||
; Tests from binary32.cton affected by allones_funcaddrs.
|
; Tests from binary32.cton affected by allones_funcaddrs.
|
||||||
function %I32() {
|
function %I32() {
|
||||||
sig0 = ()
|
sig0 = ()
|
||||||
fn0 = function %foo()
|
fn0 = %foo()
|
||||||
|
|
||||||
ebb0:
|
ebb0:
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ isa x86 haswell
|
|||||||
; Tests from binary64.cton affected by allones_funcaddrs.
|
; Tests from binary64.cton affected by allones_funcaddrs.
|
||||||
function %I64() {
|
function %I64() {
|
||||||
sig0 = ()
|
sig0 = ()
|
||||||
fn0 = function %foo()
|
fn0 = %foo()
|
||||||
|
|
||||||
ebb0:
|
ebb0:
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ isa x86 haswell
|
|||||||
|
|
||||||
function %I32() {
|
function %I32() {
|
||||||
sig0 = ()
|
sig0 = ()
|
||||||
fn0 = function %foo()
|
fn0 = %foo()
|
||||||
|
|
||||||
gv0 = globalsym %some_gv
|
gv0 = globalsym %some_gv
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ isa x86 haswell
|
|||||||
; Tests for i64 instructions.
|
; Tests for i64 instructions.
|
||||||
function %I64() {
|
function %I64() {
|
||||||
sig0 = ()
|
sig0 = ()
|
||||||
fn0 = function %foo()
|
fn0 = %foo()
|
||||||
|
fn1 = colocated %bar()
|
||||||
|
|
||||||
gv0 = globalsym %some_gv
|
gv0 = globalsym %some_gv
|
||||||
|
gv1 = globalsym colocated %some_gv
|
||||||
|
|
||||||
; Use incoming_arg stack slots because they won't be relocated by the frame
|
; Use incoming_arg stack slots because they won't be relocated by the frame
|
||||||
; layout.
|
; layout.
|
||||||
@@ -29,6 +31,9 @@ ebb0:
|
|||||||
; asm: call foo@PLT
|
; asm: call foo@PLT
|
||||||
call fn0() ; bin: e8 PLTRel4(%foo-4) 00000000
|
call fn0() ; bin: e8 PLTRel4(%foo-4) 00000000
|
||||||
|
|
||||||
|
; asm: call foo
|
||||||
|
call fn1() ; bin: e8 PCRel4(%bar-4) 00000000
|
||||||
|
|
||||||
; asm: mov 0x0(%rip), %rax
|
; asm: mov 0x0(%rip), %rax
|
||||||
[-,%rax] v0 = func_addr.i64 fn0 ; bin: 48 8b 05 GOTPCRel4(%foo-4) 00000000
|
[-,%rax] v0 = func_addr.i64 fn0 ; bin: 48 8b 05 GOTPCRel4(%foo-4) 00000000
|
||||||
; asm: mov 0x0(%rip), %rsi
|
; asm: mov 0x0(%rip), %rsi
|
||||||
@@ -50,5 +55,12 @@ ebb0:
|
|||||||
; asm: mov 0x0(%rip), %r10
|
; asm: mov 0x0(%rip), %r10
|
||||||
[-,%r10] v5 = globalsym_addr.i64 gv0 ; bin: 4c 8b 15 GOTPCRel4(%some_gv-4) 00000000
|
[-,%r10] v5 = globalsym_addr.i64 gv0 ; bin: 4c 8b 15 GOTPCRel4(%some_gv-4) 00000000
|
||||||
|
|
||||||
|
; asm: lea 0x0(%rip), %rcx
|
||||||
|
[-,%rcx] v6 = globalsym_addr.i64 gv1 ; bin: 48 8d 0d PCRel4(%some_gv-4) 00000000
|
||||||
|
; asm: lea 0x0(%rip), %rsi
|
||||||
|
[-,%rsi] v7 = globalsym_addr.i64 gv1 ; bin: 48 8d 35 PCRel4(%some_gv-4) 00000000
|
||||||
|
; asm: lea 0x0(%rip), %r10
|
||||||
|
[-,%r10] v8 = globalsym_addr.i64 gv1 ; bin: 4c 8d 15 PCRel4(%some_gv-4) 00000000
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ isa x86 haswell
|
|||||||
; Tests for i64 instructions.
|
; Tests for i64 instructions.
|
||||||
function %I64() {
|
function %I64() {
|
||||||
sig0 = ()
|
sig0 = ()
|
||||||
fn0 = function %foo()
|
fn0 = %foo()
|
||||||
|
|
||||||
gv0 = globalsym %some_gv
|
gv0 = globalsym %some_gv
|
||||||
|
|
||||||
@@ -473,12 +473,6 @@ ebb0:
|
|||||||
; asm: movzbq %dl, %rsi
|
; asm: movzbq %dl, %rsi
|
||||||
[-,%rsi] v351 = bint.i64 v301 ; bin: 0f b6 f2
|
[-,%rsi] v351 = bint.i64 v301 ; bin: 0f b6 f2
|
||||||
|
|
||||||
; TODO: x86-64 can't encode a direct call to an arbitrary 64-bit address in
|
|
||||||
; a single instruction. When we add a concept of colocated definitions, this
|
|
||||||
; test can be re-enabled.
|
|
||||||
; disabled: asm: call foo
|
|
||||||
; disabled: call fn0() ; bin: e8 PCRel4(%foo-4) 00000000
|
|
||||||
|
|
||||||
; asm: movabsq $0, %rcx
|
; asm: movabsq $0, %rcx
|
||||||
[-,%rcx] v400 = func_addr.i64 fn0 ; bin: 48 b9 Abs8(%foo) 0000000000000000
|
[-,%rcx] v400 = func_addr.i64 fn0 ; bin: 48 b9 Abs8(%foo) 0000000000000000
|
||||||
; asm: movabsq $0, %rsi
|
; asm: movabsq $0, %rsi
|
||||||
@@ -713,7 +707,7 @@ ebb0:
|
|||||||
; be done by an instruction shrinking pass.
|
; be done by an instruction shrinking pass.
|
||||||
function %I32() {
|
function %I32() {
|
||||||
sig0 = ()
|
sig0 = ()
|
||||||
fn0 = function %foo()
|
fn0 = %foo()
|
||||||
|
|
||||||
ss0 = incoming_arg 8, offset 0
|
ss0 = incoming_arg 8, offset 0
|
||||||
ss1 = incoming_arg 1024, offset -1024
|
ss1 = incoming_arg 1024, offset -1024
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ ebb0(v0: f32):
|
|||||||
}
|
}
|
||||||
; check: function %floor(f32 [%xmm0]) -> f32 [%xmm0] system_v {
|
; check: function %floor(f32 [%xmm0]) -> f32 [%xmm0] system_v {
|
||||||
; check: sig0 = (f32) -> f32 system_v
|
; check: sig0 = (f32) -> f32 system_v
|
||||||
; check: fn0 = sig0 %FloorF32
|
; check: fn0 = %FloorF32 sig0
|
||||||
; check: v1 = call fn0(v0)
|
; check: v1 = call fn0(v0)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ ebb0:
|
|||||||
; A function performing a call.
|
; A function performing a call.
|
||||||
|
|
||||||
function %call() {
|
function %call() {
|
||||||
fn0 = function %foo()
|
fn0 = %foo()
|
||||||
|
|
||||||
ebb0:
|
ebb0:
|
||||||
call fn0()
|
call fn0()
|
||||||
@@ -55,7 +55,7 @@ ebb0:
|
|||||||
; check: function %call(i64 fp [%rbp]) -> i64 fp [%rbp] system_v {
|
; check: function %call(i64 fp [%rbp]) -> i64 fp [%rbp] system_v {
|
||||||
; nextln: ss0 = incoming_arg 16, offset -16
|
; nextln: ss0 = incoming_arg 16, offset -16
|
||||||
; nextln: sig0 = () system_v
|
; nextln: sig0 = () system_v
|
||||||
; nextln: fn0 = sig0 %foo
|
; nextln: fn0 = %foo sig0
|
||||||
; nextln:
|
; nextln:
|
||||||
; nextln: ebb0(v0: i64 [%rbp]):
|
; nextln: ebb0(v0: i64 [%rbp]):
|
||||||
; nextln: x86_push v0
|
; nextln: x86_push v0
|
||||||
|
|||||||
@@ -26,22 +26,22 @@ ebb1:
|
|||||||
function %signatures() {
|
function %signatures() {
|
||||||
sig10 = ()
|
sig10 = ()
|
||||||
sig11 = (i32, f64) -> i32, b1 spiderwasm
|
sig11 = (i32, f64) -> i32, b1 spiderwasm
|
||||||
fn5 = sig11 %foo
|
fn5 = %foo sig11
|
||||||
fn8 = function %bar(i32) -> b1
|
fn8 = %bar(i32) -> b1
|
||||||
}
|
}
|
||||||
; sameln: function %signatures() system_v {
|
; sameln: function %signatures() system_v {
|
||||||
; check: sig10 = () system_v
|
; check: sig10 = () system_v
|
||||||
; check: sig11 = (i32, f64) -> i32, b1 spiderwasm
|
; check: sig11 = (i32, f64) -> i32, b1 spiderwasm
|
||||||
; check: sig12 = (i32) -> b1 system_v
|
; check: sig12 = (i32) -> b1 system_v
|
||||||
; not: fn0
|
; not: fn0
|
||||||
; check: fn5 = sig11 %foo
|
; check: fn5 = %foo sig11
|
||||||
; check: fn8 = sig12 %bar
|
; check: fn8 = %bar sig12
|
||||||
; check: }
|
; check: }
|
||||||
|
|
||||||
function %direct() {
|
function %direct() {
|
||||||
fn0 = function %none()
|
fn0 = %none()
|
||||||
fn1 = function %one() -> i32
|
fn1 = %one() -> i32
|
||||||
fn2 = function %two() -> i32, f32
|
fn2 = %two() -> i32, f32
|
||||||
|
|
||||||
ebb0:
|
ebb0:
|
||||||
call fn0()
|
call fn0()
|
||||||
@@ -72,7 +72,7 @@ ebb0(v0: i64):
|
|||||||
|
|
||||||
function %long_call() {
|
function %long_call() {
|
||||||
sig0 = ()
|
sig0 = ()
|
||||||
fn0 = sig0 %none
|
fn0 = %none sig0
|
||||||
|
|
||||||
ebb0:
|
ebb0:
|
||||||
v0 = func_addr.i32 fn0
|
v0 = func_addr.i32 fn0
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ ebb1(v10: i32):
|
|||||||
|
|
||||||
; Pass an EBB argument as a function argument.
|
; Pass an EBB argument as a function argument.
|
||||||
function %callebb(i32, i32) -> i32 {
|
function %callebb(i32, i32) -> i32 {
|
||||||
fn0 = function %foo(i32) -> i32
|
fn0 = %foo(i32) -> i32
|
||||||
|
|
||||||
ebb0(v1: i32, v2: i32):
|
ebb0(v1: i32, v2: i32):
|
||||||
brnz v1, ebb1(v1)
|
brnz v1, ebb1(v1)
|
||||||
@@ -66,7 +66,7 @@ ebb1(v10: i32):
|
|||||||
|
|
||||||
; Pass an EBB argument as a jump argument.
|
; Pass an EBB argument as a jump argument.
|
||||||
function %jumpebb(i32, i32) -> i32 {
|
function %jumpebb(i32, i32) -> i32 {
|
||||||
fn0 = function %foo(i32) -> i32
|
fn0 = %foo(i32) -> i32
|
||||||
|
|
||||||
ebb0(v1: i32, v2: i32):
|
ebb0(v1: i32, v2: i32):
|
||||||
brnz v1, ebb1(v1, v2)
|
brnz v1, ebb1(v1, v2)
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ function %pr207(i64 vmctx, i32, i32) -> i32 system_v {
|
|||||||
sig0 = (i64 vmctx, i32, i32) -> i32 system_v
|
sig0 = (i64 vmctx, i32, i32) -> i32 system_v
|
||||||
sig1 = (i64 vmctx, i32, i32, i32) -> i32 system_v
|
sig1 = (i64 vmctx, i32, i32, i32) -> i32 system_v
|
||||||
sig2 = (i64 vmctx, i32, i32, i32) -> i32 system_v
|
sig2 = (i64 vmctx, i32, i32, i32) -> i32 system_v
|
||||||
fn0 = sig0 u0:2
|
fn0 = u0:2 sig0
|
||||||
fn1 = sig1 u0:0
|
fn1 = u0:0 sig1
|
||||||
fn2 = sig2 u0:1
|
fn2 = u0:1 sig2
|
||||||
|
|
||||||
ebb0(v0: i64, v1: i32, v2: i32):
|
ebb0(v0: i64, v1: i32, v2: i32):
|
||||||
v3 = iconst.i32 0
|
v3 = iconst.i32 0
|
||||||
@@ -1038,7 +1038,7 @@ function %musl(f64 [%xmm0], i64 vmctx [%rdi]) -> f64 [%xmm0] system_v {
|
|||||||
gv0 = vmctx
|
gv0 = vmctx
|
||||||
heap0 = static gv0, min 0, bound 0x0001_0000_0000, guard 0x8000_0000
|
heap0 = static gv0, min 0, bound 0x0001_0000_0000, guard 0x8000_0000
|
||||||
sig0 = (f64 [%xmm0], i32 [%rdi], i64 vmctx [%rsi]) -> f64 [%xmm0] system_v
|
sig0 = (f64 [%xmm0], i32 [%rdi], i64 vmctx [%rsi]) -> f64 [%xmm0] system_v
|
||||||
fn0 = sig0 u0:517
|
fn0 = u0:517 sig0
|
||||||
|
|
||||||
ebb0(v0: f64, v1: i64):
|
ebb0(v0: f64, v1: i64):
|
||||||
v3 = iconst.i64 0
|
v3 = iconst.i64 0
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ function %pr208(i64 vmctx [%rdi]) system_v {
|
|||||||
heap0 = static gv0, min 0, bound 0x5000, guard 0x0040_0000
|
heap0 = static gv0, min 0, bound 0x5000, guard 0x0040_0000
|
||||||
sig0 = (i64 vmctx [%rdi]) -> i32 [%rax] system_v
|
sig0 = (i64 vmctx [%rdi]) -> i32 [%rax] system_v
|
||||||
sig1 = (i64 vmctx [%rdi], i32 [%rsi]) system_v
|
sig1 = (i64 vmctx [%rdi], i32 [%rsi]) system_v
|
||||||
fn0 = sig0 u0:1
|
fn0 = u0:1 sig0
|
||||||
fn1 = sig1 u0:3
|
fn1 = u0:3 sig1
|
||||||
|
|
||||||
ebb0(v0: i64):
|
ebb0(v0: i64):
|
||||||
v1 = iconst.i32 0
|
v1 = iconst.i32 0
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ isa riscv enable_e
|
|||||||
|
|
||||||
; Check that we can handle a function return value that got spilled.
|
; Check that we can handle a function return value that got spilled.
|
||||||
function %spill_return() -> i32 {
|
function %spill_return() -> i32 {
|
||||||
fn0 = function %foo() -> i32 system_v
|
fn0 = %foo() -> i32 system_v
|
||||||
|
|
||||||
ebb0:
|
ebb0:
|
||||||
v0 = call fn0()
|
v0 = call fn0()
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ ebb0(v1: i32):
|
|||||||
|
|
||||||
; All values live across a call must be spilled
|
; All values live across a call must be spilled
|
||||||
function %across_call(i32) {
|
function %across_call(i32) {
|
||||||
fn0 = function %foo(i32)
|
fn0 = %foo(i32)
|
||||||
ebb0(v1: i32):
|
ebb0(v1: i32):
|
||||||
; check: v1 = spill
|
; check: v1 = spill
|
||||||
call fn0(v1)
|
call fn0(v1)
|
||||||
@@ -83,7 +83,7 @@ ebb0(v1: i32):
|
|||||||
|
|
||||||
; The same value used for two function arguments.
|
; The same value used for two function arguments.
|
||||||
function %doubleuse(i32) {
|
function %doubleuse(i32) {
|
||||||
fn0 = function %xx(i32, i32)
|
fn0 = %xx(i32, i32)
|
||||||
ebb0(v0: i32):
|
ebb0(v0: i32):
|
||||||
; check: $(c=$V) = copy v0
|
; check: $(c=$V) = copy v0
|
||||||
call fn0(v0, v0)
|
call fn0(v0, v0)
|
||||||
|
|||||||
@@ -42,14 +42,14 @@ function %type_mismatch_controlling_variable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function %fn_call_too_few_args() {
|
function %fn_call_too_few_args() {
|
||||||
fn2 = function %great_fn(i32, f32)
|
fn2 = %great_fn(i32, f32)
|
||||||
ebb0:
|
ebb0:
|
||||||
call fn2() ; error: mismatched argument count, got 0, expected 2
|
call fn2() ; error: mismatched argument count, got 0, expected 2
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
function %fn_call_too_many_args() {
|
function %fn_call_too_many_args() {
|
||||||
fn5 = function %best_fn()
|
fn5 = %best_fn()
|
||||||
ebb0:
|
ebb0:
|
||||||
v0 = iconst.i64 56
|
v0 = iconst.i64 56
|
||||||
v1 = f32const 0.0
|
v1 = f32const 0.0
|
||||||
|
|||||||
35
lib/cretonne/meta/base/predicates.py
Normal file
35
lib/cretonne/meta/base/predicates.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
Cretonne predicates that consider `Function` fields.
|
||||||
|
"""
|
||||||
|
from cdsl.predicates import FieldPredicate
|
||||||
|
from .formats import UnaryGlobalVar
|
||||||
|
|
||||||
|
try:
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from cdsl.formats import FormatField # noqa
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class IsColocatedFunc(FieldPredicate):
|
||||||
|
"""
|
||||||
|
An instruction predicate that checks the referenced function is colocated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, field):
|
||||||
|
# type: (FormatField) -> None
|
||||||
|
super(IsColocatedFunc, self).__init__(
|
||||||
|
field, 'is_colocated_func', ('func',))
|
||||||
|
|
||||||
|
|
||||||
|
class IsColocatedData(FieldPredicate):
|
||||||
|
"""
|
||||||
|
An instruction predicate that checks the referenced data object is
|
||||||
|
colocated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# type: () -> None
|
||||||
|
super(IsColocatedData, self).__init__(
|
||||||
|
UnaryGlobalVar.global_var, 'is_colocated_data', ('func',))
|
||||||
@@ -370,9 +370,9 @@ class TypePredicate(object):
|
|||||||
"""
|
"""
|
||||||
Return Rust code for evaluating this predicate.
|
Return Rust code for evaluating this predicate.
|
||||||
|
|
||||||
It is assumed that the context has `dfg` and `args` variables.
|
It is assumed that the context has `func` and `args` variables.
|
||||||
"""
|
"""
|
||||||
return 'dfg.value_type(args[{}]) == {}'.format(
|
return 'func.dfg.value_type(args[{}]) == {}'.format(
|
||||||
self.value_arg, self.value_type.rust_name())
|
self.value_arg, self.value_type.rust_name())
|
||||||
|
|
||||||
|
|
||||||
@@ -409,7 +409,7 @@ class CtrlTypePredicate(object):
|
|||||||
"""
|
"""
|
||||||
Return Rust code for evaluating this predicate.
|
Return Rust code for evaluating this predicate.
|
||||||
|
|
||||||
It is assumed that the context has `dfg` and `inst` variables.
|
It is assumed that the context has `func` and `inst` variables.
|
||||||
"""
|
"""
|
||||||
return 'dfg.ctrl_typevar(inst) == {}'.format(
|
return 'func.dfg.ctrl_typevar(inst) == {}'.format(
|
||||||
self.value_type.rust_name())
|
self.value_type.rust_name())
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ except ImportError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def emit_instp(instp, fmt, has_dfg=False):
|
def emit_instp(instp, fmt, has_func=False):
|
||||||
# type: (PredNode, srcgen.Formatter, bool) -> None
|
# type: (PredNode, srcgen.Formatter, bool) -> None
|
||||||
"""
|
"""
|
||||||
Emit code for matching an instruction predicate against an
|
Emit code for matching an instruction predicate against an
|
||||||
@@ -87,7 +87,7 @@ def emit_instp(instp, fmt, has_dfg=False):
|
|||||||
|
|
||||||
# Deal with pure type check predicates which apply to any instruction.
|
# Deal with pure type check predicates which apply to any instruction.
|
||||||
if iform == instruction_context:
|
if iform == instruction_context:
|
||||||
fmt.line('let args = inst.arguments(&dfg.value_lists);')
|
fmt.line('let args = inst.arguments(&func.dfg.value_lists);')
|
||||||
fmt.line(instp.rust_predicate(0))
|
fmt.line(instp.rust_predicate(0))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -114,11 +114,11 @@ def emit_instp(instp, fmt, has_dfg=False):
|
|||||||
.format(iform.name, fields), '}'):
|
.format(iform.name, fields), '}'):
|
||||||
if has_type_check:
|
if has_type_check:
|
||||||
# We could implement this if we need to.
|
# We could implement this if we need to.
|
||||||
assert has_dfg, "Recipe predicates can't check type variables."
|
assert has_func, "Recipe predicates can't check type variables."
|
||||||
fmt.line('let args = inst.arguments(&dfg.value_lists);')
|
fmt.line('let args = inst.arguments(&func.dfg.value_lists);')
|
||||||
elif has_dfg:
|
elif has_func:
|
||||||
# Silence dead argument warning.
|
# Silence dead argument warning.
|
||||||
fmt.line('let _ = dfg;')
|
fmt.line('let _ = func;')
|
||||||
fmt.format('return {};', instp.rust_predicate(0))
|
fmt.format('return {};', instp.rust_predicate(0))
|
||||||
fmt.line('unreachable!();')
|
fmt.line('unreachable!();')
|
||||||
|
|
||||||
@@ -132,9 +132,9 @@ def emit_inst_predicates(instps, fmt):
|
|||||||
for instp, number in instps.items():
|
for instp, number in instps.items():
|
||||||
name = 'inst_predicate_{}'.format(number)
|
name = 'inst_predicate_{}'.format(number)
|
||||||
with fmt.indented(
|
with fmt.indented(
|
||||||
'fn {}(dfg: &ir::DataFlowGraph, inst: &ir::InstructionData)'
|
'fn {}(func: &ir::Function, inst: &ir::InstructionData)'
|
||||||
'-> bool {{'.format(name), '}'):
|
'-> bool {{'.format(name), '}'):
|
||||||
emit_instp(instp, fmt, has_dfg=True)
|
emit_instp(instp, fmt, has_func=True)
|
||||||
|
|
||||||
# Generate the static table.
|
# Generate the static table.
|
||||||
with fmt.indented(
|
with fmt.indented(
|
||||||
|
|||||||
@@ -185,9 +185,9 @@ def unwrap_inst(iref, node, fmt):
|
|||||||
fmt.line('ref args,')
|
fmt.line('ref args,')
|
||||||
fmt.line('..')
|
fmt.line('..')
|
||||||
fmt.outdented_line('} = pos.func.dfg[inst] {')
|
fmt.outdented_line('} = pos.func.dfg[inst] {')
|
||||||
fmt.line('let dfg = &pos.func.dfg;')
|
fmt.line('let func = &pos.func;')
|
||||||
if iform.has_value_list:
|
if iform.has_value_list:
|
||||||
fmt.line('let args = args.as_slice(&dfg.value_lists);')
|
fmt.line('let args = args.as_slice(&func.dfg.value_lists);')
|
||||||
elif nvops == 1:
|
elif nvops == 1:
|
||||||
fmt.line('let args = [arg];')
|
fmt.line('let args = [arg];')
|
||||||
# Generate the values for the tuple.
|
# Generate the values for the tuple.
|
||||||
@@ -198,7 +198,7 @@ def unwrap_inst(iref, node, fmt):
|
|||||||
fmt.format('{},', iform.imm_fields[n].member)
|
fmt.format('{},', iform.imm_fields[n].member)
|
||||||
elif op.is_value():
|
elif op.is_value():
|
||||||
n = expr.inst.value_opnums.index(opnum)
|
n = expr.inst.value_opnums.index(opnum)
|
||||||
fmt.format('dfg.resolve_aliases(args[{}]),', n)
|
fmt.format('func.dfg.resolve_aliases(args[{}]),', n)
|
||||||
# Evaluate the instruction predicate, if any.
|
# Evaluate the instruction predicate, if any.
|
||||||
instp = expr.inst_predicate_with_ctrl_typevar()
|
instp = expr.inst_predicate_with_ctrl_typevar()
|
||||||
fmt.line(instp.rust_predicate(0) if instp else 'true')
|
fmt.line(instp.rust_predicate(0) if instp else 'true')
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ x86 Encodings.
|
|||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from cdsl.predicates import IsUnsignedInt, Not, And
|
from cdsl.predicates import IsUnsignedInt, Not, And
|
||||||
|
from base.predicates import IsColocatedFunc, IsColocatedData
|
||||||
from base import instructions as base
|
from base import instructions as base
|
||||||
from base.formats import UnaryImm
|
from base.formats import UnaryImm, FuncAddr, Call
|
||||||
from .defs import X86_64, X86_32
|
from .defs import X86_64, X86_32
|
||||||
from . import recipes as r
|
from . import recipes as r
|
||||||
from . import settings as cfg
|
from . import settings as cfg
|
||||||
@@ -292,16 +293,22 @@ enc_both(base.regspill.f64, r.fregspill32, 0xf2, 0x0f, 0x11)
|
|||||||
# Function addresses.
|
# Function addresses.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Non-PIC, all-ones funcaddresses.
|
||||||
X86_32.enc(base.func_addr.i32, *r.fnaddr4(0xb8),
|
X86_32.enc(base.func_addr.i32, *r.fnaddr4(0xb8),
|
||||||
isap=And(Not(allones_funcaddrs), Not(is_pic)))
|
isap=And(Not(allones_funcaddrs), Not(is_pic)))
|
||||||
X86_64.enc(base.func_addr.i64, *r.fnaddr8.rex(0xb8, w=1),
|
X86_64.enc(base.func_addr.i64, *r.fnaddr8.rex(0xb8, w=1),
|
||||||
isap=And(Not(allones_funcaddrs), Not(is_pic)))
|
isap=And(Not(allones_funcaddrs), Not(is_pic)))
|
||||||
|
|
||||||
|
# Non-PIC, all-zeros funcaddresses.
|
||||||
X86_32.enc(base.func_addr.i32, *r.allones_fnaddr4(0xb8),
|
X86_32.enc(base.func_addr.i32, *r.allones_fnaddr4(0xb8),
|
||||||
isap=And(allones_funcaddrs, Not(is_pic)))
|
isap=And(allones_funcaddrs, Not(is_pic)))
|
||||||
X86_64.enc(base.func_addr.i64, *r.allones_fnaddr8.rex(0xb8, w=1),
|
X86_64.enc(base.func_addr.i64, *r.allones_fnaddr8.rex(0xb8, w=1),
|
||||||
isap=And(allones_funcaddrs, Not(is_pic)))
|
isap=And(allones_funcaddrs, Not(is_pic)))
|
||||||
|
|
||||||
|
# PIC
|
||||||
|
X86_64.enc(base.func_addr.i64, *r.pcrel_fnaddr8.rex(0x8d, w=1),
|
||||||
|
isap=is_pic,
|
||||||
|
instp=IsColocatedFunc(FuncAddr.func_ref))
|
||||||
X86_64.enc(base.func_addr.i64, *r.got_fnaddr8.rex(0x8b, w=1),
|
X86_64.enc(base.func_addr.i64, *r.got_fnaddr8.rex(0x8b, w=1),
|
||||||
isap=is_pic)
|
isap=is_pic)
|
||||||
|
|
||||||
@@ -309,18 +316,34 @@ X86_64.enc(base.func_addr.i64, *r.got_fnaddr8.rex(0x8b, w=1),
|
|||||||
# Global addresses.
|
# Global addresses.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Non-PIC
|
||||||
X86_32.enc(base.globalsym_addr.i32, *r.gvaddr4(0xb8),
|
X86_32.enc(base.globalsym_addr.i32, *r.gvaddr4(0xb8),
|
||||||
isap=Not(is_pic))
|
isap=Not(is_pic))
|
||||||
X86_64.enc(base.globalsym_addr.i64, *r.gvaddr8.rex(0xb8, w=1),
|
X86_64.enc(base.globalsym_addr.i64, *r.gvaddr8.rex(0xb8, w=1),
|
||||||
isap=Not(is_pic))
|
isap=Not(is_pic))
|
||||||
|
|
||||||
|
# PIC, colocated
|
||||||
|
X86_64.enc(base.globalsym_addr.i64, *r.pcrel_gvaddr8.rex(0x8d, w=1),
|
||||||
|
isap=is_pic,
|
||||||
|
instp=IsColocatedData())
|
||||||
|
|
||||||
|
# PIC, non-colocated
|
||||||
X86_64.enc(base.globalsym_addr.i64, *r.got_gvaddr8.rex(0x8b, w=1),
|
X86_64.enc(base.globalsym_addr.i64, *r.got_gvaddr8.rex(0x8b, w=1),
|
||||||
isap=is_pic)
|
isap=is_pic)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Call/return
|
# Call/return
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# 32-bit, both PIC and non-PIC.
|
||||||
X86_32.enc(base.call, *r.call_id(0xe8))
|
X86_32.enc(base.call, *r.call_id(0xe8))
|
||||||
|
|
||||||
|
# 64-bit, PIC, colocated and non-colocated. There is no 64-bit non-PIC, since
|
||||||
|
# non-PIC is currently using the large model, which requires calls be lowered
|
||||||
|
# to func_addr+call_indirect.
|
||||||
|
X86_64.enc(base.call, *r.call_id(0xe8),
|
||||||
|
isap=is_pic,
|
||||||
|
instp=IsColocatedFunc(Call.func_ref))
|
||||||
X86_64.enc(base.call, *r.call_plt_id(0xe8), isap=is_pic)
|
X86_64.enc(base.call, *r.call_plt_id(0xe8), isap=is_pic)
|
||||||
|
|
||||||
X86_32.enc(base.call_indirect.i32, *r.call_r(0xff, rrr=2))
|
X86_32.enc(base.call_indirect.i32, *r.call_r(0xff, rrr=2))
|
||||||
|
|||||||
@@ -631,6 +631,21 @@ allones_fnaddr8 = TailRecipe(
|
|||||||
sink.put8(!0);
|
sink.put8(!0);
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
pcrel_fnaddr8 = TailRecipe(
|
||||||
|
'pcrel_fnaddr8', FuncAddr, size=5, ins=(), outs=GPR,
|
||||||
|
# rex2 gets passed 0 for r/m register because the upper bit of
|
||||||
|
# r/m doesnt get decoded when in rip-relative addressing mode.
|
||||||
|
emit='''
|
||||||
|
PUT_OP(bits, rex2(0, out_reg0), sink);
|
||||||
|
modrm_riprel(out_reg0, sink);
|
||||||
|
// The addend adjusts for the difference between the end of the
|
||||||
|
// instruction and the beginning of the immediate field.
|
||||||
|
sink.reloc_external(Reloc::X86PCRel4,
|
||||||
|
&func.dfg.ext_funcs[func_ref].name,
|
||||||
|
-4);
|
||||||
|
sink.put4(0);
|
||||||
|
''')
|
||||||
|
|
||||||
got_fnaddr8 = TailRecipe(
|
got_fnaddr8 = TailRecipe(
|
||||||
'got_fnaddr8', FuncAddr, size=5, ins=(), outs=GPR,
|
'got_fnaddr8', FuncAddr, size=5, ins=(), outs=GPR,
|
||||||
# rex2 gets passed 0 for r/m register because the upper bit of
|
# rex2 gets passed 0 for r/m register because the upper bit of
|
||||||
@@ -669,6 +684,20 @@ gvaddr8 = TailRecipe(
|
|||||||
sink.put8(0);
|
sink.put8(0);
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
# XX+rd iq with PCRel4 globalsym relocation.
|
||||||
|
pcrel_gvaddr8 = TailRecipe(
|
||||||
|
'pcrel_gvaddr8', UnaryGlobalVar, size=5, ins=(), outs=GPR,
|
||||||
|
emit='''
|
||||||
|
PUT_OP(bits, rex2(0, out_reg0), sink);
|
||||||
|
modrm_rm(5, out_reg0, sink);
|
||||||
|
// The addend adjusts for the difference between the end of the
|
||||||
|
// instruction and the beginning of the immediate field.
|
||||||
|
sink.reloc_external(Reloc::X86PCRel4,
|
||||||
|
&func.global_vars[global_var].symbol_name(),
|
||||||
|
-4);
|
||||||
|
sink.put4(0);
|
||||||
|
''')
|
||||||
|
|
||||||
# XX+rd iq with Abs8 globalsym relocation.
|
# XX+rd iq with Abs8 globalsym relocation.
|
||||||
got_gvaddr8 = TailRecipe(
|
got_gvaddr8 = TailRecipe(
|
||||||
'got_gvaddr8', UnaryGlobalVar, size=5, ins=(), outs=GPR,
|
'got_gvaddr8', UnaryGlobalVar, size=5, ins=(), outs=GPR,
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ fn relax_branch(
|
|||||||
// Pick the first encoding that can handle the branch range.
|
// Pick the first encoding that can handle the branch range.
|
||||||
let dfg = &cur.func.dfg;
|
let dfg = &cur.func.dfg;
|
||||||
let ctrl_type = dfg.ctrl_typevar(inst);
|
let ctrl_type = dfg.ctrl_typevar(inst);
|
||||||
if let Some(enc) = isa.legal_encodings(dfg, &dfg[inst], ctrl_type).find(
|
if let Some(enc) = isa.legal_encodings(cur.func, &dfg[inst], ctrl_type).find(
|
||||||
|&enc| {
|
|&enc| {
|
||||||
let range = encinfo.branch_range(enc).expect("Branch with no range");
|
let range = encinfo.branch_range(enc).expect("Branch with no range");
|
||||||
if !range.contains(offset, dest_offset) {
|
if !range.contains(offset, dest_offset) {
|
||||||
|
|||||||
@@ -747,7 +747,7 @@ impl<'c, 'f> ir::InstInserterBase<'c> for &'c mut EncCursor<'f> {
|
|||||||
// XXX Is there a way to describe this error to the user?
|
// XXX Is there a way to describe this error to the user?
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(match_wild_err_arm))]
|
#[cfg_attr(feature = "cargo-clippy", allow(match_wild_err_arm))]
|
||||||
match self.isa.encode(
|
match self.isa.encode(
|
||||||
&self.func.dfg,
|
&self.func,
|
||||||
&self.func.dfg[inst],
|
&self.func.dfg[inst],
|
||||||
ctrl_typevar,
|
ctrl_typevar,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use ir::extfunc::ExtFuncData;
|
|||||||
use ir::instructions::{BranchInfo, CallInfo, InstructionData};
|
use ir::instructions::{BranchInfo, CallInfo, InstructionData};
|
||||||
use ir::types;
|
use ir::types;
|
||||||
use ir::{Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueList, ValueListPool};
|
use ir::{Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueList, ValueListPool};
|
||||||
use isa::{Encoding, Legalize, TargetIsa};
|
use isa::TargetIsa;
|
||||||
use packed_option::ReservedValue;
|
use packed_option::ReservedValue;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
@@ -660,12 +660,6 @@ impl DataFlowGraph {
|
|||||||
self.value_type(self.first_result(inst))
|
self.value_type(self.first_result(inst))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around `TargetIsa::encode` for encoding an existing instruction
|
|
||||||
/// in the `DataFlowGraph`.
|
|
||||||
pub fn encode(&self, inst: Inst, isa: &TargetIsa) -> Result<Encoding, Legalize> {
|
|
||||||
isa.encode(&self, &self[inst], self.ctrl_typevar(inst))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allow immutable access to instructions via indexing.
|
/// Allow immutable access to instructions via indexing.
|
||||||
|
|||||||
@@ -327,11 +327,18 @@ pub struct ExtFuncData {
|
|||||||
pub name: ExternalName,
|
pub name: ExternalName,
|
||||||
/// Call signature of function.
|
/// Call signature of function.
|
||||||
pub signature: SigRef,
|
pub signature: SigRef,
|
||||||
|
/// Will this function be defined nearby, such that it will always be a certain distance away,
|
||||||
|
/// after linking? If so, references to it can avoid going through a GOT or PLT. Note that
|
||||||
|
/// symbols meant to be preemptible cannot be considered colocated.
|
||||||
|
pub colocated: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ExtFuncData {
|
impl fmt::Display for ExtFuncData {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{} {}", self.signature, self.name)
|
if self.colocated {
|
||||||
|
write!(f, "colocated ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{} {}", self.name, self.signature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use ir::{CallConv, DataFlowGraph, ExternalName, Layout, Signature};
|
|||||||
use ir::{Ebb, ExtFuncData, FuncRef, GlobalVar, GlobalVarData, Heap, HeapData, JumpTable,
|
use ir::{Ebb, ExtFuncData, FuncRef, GlobalVar, GlobalVarData, Heap, HeapData, JumpTable,
|
||||||
JumpTableData, SigRef, StackSlot, StackSlotData};
|
JumpTableData, SigRef, StackSlot, StackSlotData};
|
||||||
use ir::{EbbOffsets, InstEncodings, JumpTables, SourceLocs, StackSlots, ValueLocations};
|
use ir::{EbbOffsets, InstEncodings, JumpTables, SourceLocs, StackSlots, ValueLocations};
|
||||||
use isa::{EncInfo, Legalize, TargetIsa};
|
use isa::{EncInfo, Legalize, TargetIsa, Encoding};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use write::write_function;
|
use write::write_function;
|
||||||
|
|
||||||
@@ -177,9 +177,15 @@ impl Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around `DataFlowGraph::encode` which assigns `inst` the resulting encoding.
|
/// Wrapper around `encode` which assigns `inst` the resulting encoding.
|
||||||
pub fn update_encoding(&mut self, inst: ir::Inst, isa: &TargetIsa) -> Result<(), Legalize> {
|
pub fn update_encoding(&mut self, inst: ir::Inst, isa: &TargetIsa) -> Result<(), Legalize> {
|
||||||
self.dfg.encode(inst, isa).map(|e| self.encodings[inst] = e)
|
self.encode(inst, isa).map(|e| self.encodings[inst] = e)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper around `TargetIsa::encode` for encoding an existing instruction
|
||||||
|
/// in the `Function`.
|
||||||
|
pub fn encode(&self, inst: ir::Inst, isa: &TargetIsa) -> Result<Encoding, Legalize> {
|
||||||
|
isa.encode(&self, &self.dfg[inst], self.dfg.ctrl_typevar(inst))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ pub enum GlobalVarData {
|
|||||||
Sym {
|
Sym {
|
||||||
/// The symbolic name.
|
/// The symbolic name.
|
||||||
name: ExternalName,
|
name: ExternalName,
|
||||||
|
|
||||||
|
/// Will this variable be defined nearby, such that it will always be a certain distance
|
||||||
|
/// away, after linking? If so, references to it can avoid going through a GOT. Note that
|
||||||
|
/// symbols meant to be preemptible cannot be colocated.
|
||||||
|
colocated: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +45,7 @@ impl GlobalVarData {
|
|||||||
/// Assume that `self` is an `GlobalVarData::Sym` and return its name.
|
/// Assume that `self` is an `GlobalVarData::Sym` and return its name.
|
||||||
pub fn symbol_name(&self) -> &ExternalName {
|
pub fn symbol_name(&self) -> &ExternalName {
|
||||||
match *self {
|
match *self {
|
||||||
GlobalVarData::Sym { ref name } => name,
|
GlobalVarData::Sym { ref name, .. } => name,
|
||||||
_ => panic!("only symbols have names"),
|
_ => panic!("only symbols have names"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,7 +56,15 @@ impl fmt::Display for GlobalVarData {
|
|||||||
match *self {
|
match *self {
|
||||||
GlobalVarData::VmCtx { offset } => write!(f, "vmctx{}", offset),
|
GlobalVarData::VmCtx { offset } => write!(f, "vmctx{}", offset),
|
||||||
GlobalVarData::Deref { base, offset } => write!(f, "deref({}){}", base, offset),
|
GlobalVarData::Deref { base, offset } => write!(f, "deref({}){}", base, offset),
|
||||||
GlobalVarData::Sym { ref name } => write!(f, "globalsym {}", name),
|
GlobalVarData::Sym {
|
||||||
|
ref name,
|
||||||
|
colocated,
|
||||||
|
} => {
|
||||||
|
if colocated {
|
||||||
|
write!(f, "colocated ")?;
|
||||||
|
}
|
||||||
|
write!(f, "globalsym {}", name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ impl TargetIsa for Isa {
|
|||||||
|
|
||||||
fn legal_encodings<'a>(
|
fn legal_encodings<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
dfg: &'a ir::DataFlowGraph,
|
func: &'a ir::Function,
|
||||||
inst: &'a ir::InstructionData,
|
inst: &'a ir::InstructionData,
|
||||||
ctrl_typevar: ir::Type,
|
ctrl_typevar: ir::Type,
|
||||||
) -> Encodings<'a> {
|
) -> Encodings<'a> {
|
||||||
lookup_enclist(
|
lookup_enclist(
|
||||||
ctrl_typevar,
|
ctrl_typevar,
|
||||||
inst,
|
inst,
|
||||||
dfg,
|
func,
|
||||||
self.cpumode,
|
self.cpumode,
|
||||||
&enc_tables::LEVEL2[..],
|
&enc_tables::LEVEL2[..],
|
||||||
&enc_tables::ENCLISTS[..],
|
&enc_tables::ENCLISTS[..],
|
||||||
|
|||||||
@@ -59,14 +59,14 @@ impl TargetIsa for Isa {
|
|||||||
|
|
||||||
fn legal_encodings<'a>(
|
fn legal_encodings<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
dfg: &'a ir::DataFlowGraph,
|
func: &'a ir::Function,
|
||||||
inst: &'a ir::InstructionData,
|
inst: &'a ir::InstructionData,
|
||||||
ctrl_typevar: ir::Type,
|
ctrl_typevar: ir::Type,
|
||||||
) -> Encodings<'a> {
|
) -> Encodings<'a> {
|
||||||
lookup_enclist(
|
lookup_enclist(
|
||||||
ctrl_typevar,
|
ctrl_typevar,
|
||||||
inst,
|
inst,
|
||||||
dfg,
|
func,
|
||||||
&enc_tables::LEVEL1_A64[..],
|
&enc_tables::LEVEL1_A64[..],
|
||||||
&enc_tables::LEVEL2[..],
|
&enc_tables::LEVEL2[..],
|
||||||
&enc_tables::ENCLISTS[..],
|
&enc_tables::ENCLISTS[..],
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
//! `lib/cretonne/meta/gen_encoding.py`.
|
//! `lib/cretonne/meta/gen_encoding.py`.
|
||||||
|
|
||||||
use constant_hash::{probe, Table};
|
use constant_hash::{probe, Table};
|
||||||
use ir::{DataFlowGraph, InstructionData, Opcode, Type};
|
use ir::{Function, InstructionData, Opcode, Type};
|
||||||
use isa::{Encoding, Legalize};
|
use isa::{Encoding, Legalize};
|
||||||
use settings::PredicateView;
|
use settings::PredicateView;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
@@ -20,7 +20,7 @@ pub type RecipePredicate = Option<fn(PredicateView, &InstructionData) -> bool>;
|
|||||||
///
|
///
|
||||||
/// This is a predicate function that needs to be tested in addition to the recipe predicate. It
|
/// This is a predicate function that needs to be tested in addition to the recipe predicate. It
|
||||||
/// can't depend on ISA settings.
|
/// can't depend on ISA settings.
|
||||||
pub type InstPredicate = fn(&DataFlowGraph, &InstructionData) -> bool;
|
pub type InstPredicate = fn(&Function, &InstructionData) -> bool;
|
||||||
|
|
||||||
/// Legalization action to perform when no encoding can be found for an instruction.
|
/// Legalization action to perform when no encoding can be found for an instruction.
|
||||||
///
|
///
|
||||||
@@ -106,7 +106,7 @@ impl<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
|
|||||||
pub fn lookup_enclist<'a, OffT1, OffT2>(
|
pub fn lookup_enclist<'a, OffT1, OffT2>(
|
||||||
ctrl_typevar: Type,
|
ctrl_typevar: Type,
|
||||||
inst: &'a InstructionData,
|
inst: &'a InstructionData,
|
||||||
dfg: &'a DataFlowGraph,
|
func: &'a Function,
|
||||||
level1_table: &'static [Level1Entry<OffT1>],
|
level1_table: &'static [Level1Entry<OffT1>],
|
||||||
level2_table: &'static [Level2Entry<OffT2>],
|
level2_table: &'static [Level2Entry<OffT2>],
|
||||||
enclist: &'static [EncListEntry],
|
enclist: &'static [EncListEntry],
|
||||||
@@ -150,7 +150,7 @@ where
|
|||||||
offset,
|
offset,
|
||||||
legalize,
|
legalize,
|
||||||
inst,
|
inst,
|
||||||
dfg,
|
func,
|
||||||
enclist,
|
enclist,
|
||||||
legalize_actions,
|
legalize_actions,
|
||||||
recipe_preds,
|
recipe_preds,
|
||||||
@@ -177,7 +177,7 @@ pub struct Encodings<'a> {
|
|||||||
// Legalization code to use of no encoding is found.
|
// Legalization code to use of no encoding is found.
|
||||||
legalize: LegalizeCode,
|
legalize: LegalizeCode,
|
||||||
inst: &'a InstructionData,
|
inst: &'a InstructionData,
|
||||||
dfg: &'a DataFlowGraph,
|
func: &'a Function,
|
||||||
enclist: &'static [EncListEntry],
|
enclist: &'static [EncListEntry],
|
||||||
legalize_actions: &'static [Legalize],
|
legalize_actions: &'static [Legalize],
|
||||||
recipe_preds: &'static [RecipePredicate],
|
recipe_preds: &'static [RecipePredicate],
|
||||||
@@ -195,7 +195,7 @@ impl<'a> Encodings<'a> {
|
|||||||
offset: usize,
|
offset: usize,
|
||||||
legalize: LegalizeCode,
|
legalize: LegalizeCode,
|
||||||
inst: &'a InstructionData,
|
inst: &'a InstructionData,
|
||||||
dfg: &'a DataFlowGraph,
|
func: &'a Function,
|
||||||
enclist: &'static [EncListEntry],
|
enclist: &'static [EncListEntry],
|
||||||
legalize_actions: &'static [Legalize],
|
legalize_actions: &'static [Legalize],
|
||||||
recipe_preds: &'static [RecipePredicate],
|
recipe_preds: &'static [RecipePredicate],
|
||||||
@@ -205,7 +205,7 @@ impl<'a> Encodings<'a> {
|
|||||||
Encodings {
|
Encodings {
|
||||||
offset,
|
offset,
|
||||||
inst,
|
inst,
|
||||||
dfg,
|
func,
|
||||||
legalize,
|
legalize,
|
||||||
isa_preds,
|
isa_preds,
|
||||||
recipe_preds,
|
recipe_preds,
|
||||||
@@ -236,7 +236,7 @@ impl<'a> Encodings<'a> {
|
|||||||
/// Check an instruction or isa predicate.
|
/// Check an instruction or isa predicate.
|
||||||
fn check_pred(&self, pred: usize) -> bool {
|
fn check_pred(&self, pred: usize) -> bool {
|
||||||
if let Some(&p) = self.inst_preds.get(pred) {
|
if let Some(&p) = self.inst_preds.get(pred) {
|
||||||
p(self.dfg, self.inst)
|
p(self.func, self.inst)
|
||||||
} else {
|
} else {
|
||||||
let pred = pred - self.inst_preds.len();
|
let pred = pred - self.inst_preds.len();
|
||||||
self.isa_preds.test(pred)
|
self.isa_preds.test(pred)
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ pub trait TargetIsa: fmt::Display {
|
|||||||
/// Returns an iterartor over legal encodings for the instruction.
|
/// Returns an iterartor over legal encodings for the instruction.
|
||||||
fn legal_encodings<'a>(
|
fn legal_encodings<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
dfg: &'a ir::DataFlowGraph,
|
func: &'a ir::Function,
|
||||||
inst: &'a ir::InstructionData,
|
inst: &'a ir::InstructionData,
|
||||||
ctrl_typevar: ir::Type,
|
ctrl_typevar: ir::Type,
|
||||||
) -> Encodings<'a>;
|
) -> Encodings<'a>;
|
||||||
@@ -180,11 +180,11 @@ pub trait TargetIsa: fmt::Display {
|
|||||||
/// This is also the main entry point for determining if an instruction is legal.
|
/// This is also the main entry point for determining if an instruction is legal.
|
||||||
fn encode(
|
fn encode(
|
||||||
&self,
|
&self,
|
||||||
dfg: &ir::DataFlowGraph,
|
func: &ir::Function,
|
||||||
inst: &ir::InstructionData,
|
inst: &ir::InstructionData,
|
||||||
ctrl_typevar: ir::Type,
|
ctrl_typevar: ir::Type,
|
||||||
) -> Result<Encoding, Legalize> {
|
) -> Result<Encoding, Legalize> {
|
||||||
let mut iter = self.legal_encodings(dfg, inst, ctrl_typevar);
|
let mut iter = self.legal_encodings(func, inst, ctrl_typevar);
|
||||||
iter.next().ok_or_else(|| iter.legalize())
|
iter.next().ok_or_else(|| iter.legalize())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ impl TargetIsa for Isa {
|
|||||||
|
|
||||||
fn legal_encodings<'a>(
|
fn legal_encodings<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
dfg: &'a ir::DataFlowGraph,
|
func: &'a ir::Function,
|
||||||
inst: &'a ir::InstructionData,
|
inst: &'a ir::InstructionData,
|
||||||
ctrl_typevar: ir::Type,
|
ctrl_typevar: ir::Type,
|
||||||
) -> Encodings<'a> {
|
) -> Encodings<'a> {
|
||||||
lookup_enclist(
|
lookup_enclist(
|
||||||
ctrl_typevar,
|
ctrl_typevar,
|
||||||
inst,
|
inst,
|
||||||
dfg,
|
func,
|
||||||
self.cpumode,
|
self.cpumode,
|
||||||
&enc_tables::LEVEL2[..],
|
&enc_tables::LEVEL2[..],
|
||||||
&enc_tables::ENCLISTS[..],
|
&enc_tables::ENCLISTS[..],
|
||||||
@@ -113,7 +113,7 @@ impl TargetIsa for Isa {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ir::{DataFlowGraph, InstructionData, Opcode};
|
use ir::{Function, InstructionData, Opcode};
|
||||||
use ir::{immediates, types};
|
use ir::{immediates, types};
|
||||||
use isa;
|
use isa;
|
||||||
use settings::{self, Configurable};
|
use settings::{self, Configurable};
|
||||||
@@ -133,10 +133,10 @@ mod tests {
|
|||||||
let shared_flags = settings::Flags::new(&shared_builder);
|
let shared_flags = settings::Flags::new(&shared_builder);
|
||||||
let isa = isa::lookup("riscv").unwrap().finish(shared_flags);
|
let isa = isa::lookup("riscv").unwrap().finish(shared_flags);
|
||||||
|
|
||||||
let mut dfg = DataFlowGraph::new();
|
let mut func = Function::new();
|
||||||
let ebb = dfg.make_ebb();
|
let ebb = func.dfg.make_ebb();
|
||||||
let arg64 = dfg.append_ebb_param(ebb, types::I64);
|
let arg64 = func.dfg.append_ebb_param(ebb, types::I64);
|
||||||
let arg32 = dfg.append_ebb_param(ebb, types::I32);
|
let arg32 = func.dfg.append_ebb_param(ebb, types::I32);
|
||||||
|
|
||||||
// Try to encode iadd_imm.i64 v1, -10.
|
// Try to encode iadd_imm.i64 v1, -10.
|
||||||
let inst64 = InstructionData::BinaryImm {
|
let inst64 = InstructionData::BinaryImm {
|
||||||
@@ -147,7 +147,7 @@ mod tests {
|
|||||||
|
|
||||||
// ADDI is I/0b00100
|
// ADDI is I/0b00100
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
encstr(&*isa, isa.encode(&dfg, &inst64, types::I64)),
|
encstr(&*isa, isa.encode(&func, &inst64, types::I64)),
|
||||||
"Ii#04"
|
"Ii#04"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Immediate is out of range for ADDI.
|
// Immediate is out of range for ADDI.
|
||||||
assert!(isa.encode(&dfg, &inst64_large, types::I64).is_err());
|
assert!(isa.encode(&func, &inst64_large, types::I64).is_err());
|
||||||
|
|
||||||
// Create an iadd_imm.i32 which is encodable in RV64.
|
// Create an iadd_imm.i32 which is encodable in RV64.
|
||||||
let inst32 = InstructionData::BinaryImm {
|
let inst32 = InstructionData::BinaryImm {
|
||||||
@@ -170,7 +170,7 @@ mod tests {
|
|||||||
|
|
||||||
// ADDIW is I/0b00110
|
// ADDIW is I/0b00110
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
encstr(&*isa, isa.encode(&dfg, &inst32, types::I32)),
|
encstr(&*isa, isa.encode(&func, &inst32, types::I32)),
|
||||||
"Ii#06"
|
"Ii#06"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -183,10 +183,10 @@ mod tests {
|
|||||||
let shared_flags = settings::Flags::new(&shared_builder);
|
let shared_flags = settings::Flags::new(&shared_builder);
|
||||||
let isa = isa::lookup("riscv").unwrap().finish(shared_flags);
|
let isa = isa::lookup("riscv").unwrap().finish(shared_flags);
|
||||||
|
|
||||||
let mut dfg = DataFlowGraph::new();
|
let mut func = Function::new();
|
||||||
let ebb = dfg.make_ebb();
|
let ebb = func.dfg.make_ebb();
|
||||||
let arg64 = dfg.append_ebb_param(ebb, types::I64);
|
let arg64 = func.dfg.append_ebb_param(ebb, types::I64);
|
||||||
let arg32 = dfg.append_ebb_param(ebb, types::I32);
|
let arg32 = func.dfg.append_ebb_param(ebb, types::I32);
|
||||||
|
|
||||||
// Try to encode iadd_imm.i64 v1, -10.
|
// Try to encode iadd_imm.i64 v1, -10.
|
||||||
let inst64 = InstructionData::BinaryImm {
|
let inst64 = InstructionData::BinaryImm {
|
||||||
@@ -196,7 +196,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// In 32-bit mode, an i64 bit add should be narrowed.
|
// In 32-bit mode, an i64 bit add should be narrowed.
|
||||||
assert!(isa.encode(&dfg, &inst64, types::I64).is_err());
|
assert!(isa.encode(&func, &inst64, types::I64).is_err());
|
||||||
|
|
||||||
// Try to encode iadd_imm.i64 v1, -10000.
|
// Try to encode iadd_imm.i64 v1, -10000.
|
||||||
let inst64_large = InstructionData::BinaryImm {
|
let inst64_large = InstructionData::BinaryImm {
|
||||||
@@ -206,7 +206,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// In 32-bit mode, an i64 bit add should be narrowed.
|
// In 32-bit mode, an i64 bit add should be narrowed.
|
||||||
assert!(isa.encode(&dfg, &inst64_large, types::I64).is_err());
|
assert!(isa.encode(&func, &inst64_large, types::I64).is_err());
|
||||||
|
|
||||||
// Create an iadd_imm.i32 which is encodable in RV32.
|
// Create an iadd_imm.i32 which is encodable in RV32.
|
||||||
let inst32 = InstructionData::BinaryImm {
|
let inst32 = InstructionData::BinaryImm {
|
||||||
@@ -217,7 +217,7 @@ mod tests {
|
|||||||
|
|
||||||
// ADDI is I/0b00100
|
// ADDI is I/0b00100
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
encstr(&*isa, isa.encode(&dfg, &inst32, types::I32)),
|
encstr(&*isa, isa.encode(&func, &inst32, types::I32)),
|
||||||
"Ii#04"
|
"Ii#04"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ mod tests {
|
|||||||
args: [arg32, arg32],
|
args: [arg32, arg32],
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(isa.encode(&dfg, &mul32, types::I32).is_err());
|
assert!(isa.encode(&func, &mul32, types::I32).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -243,16 +243,19 @@ mod tests {
|
|||||||
|
|
||||||
let isa = isa_builder.finish(shared_flags);
|
let isa = isa_builder.finish(shared_flags);
|
||||||
|
|
||||||
let mut dfg = DataFlowGraph::new();
|
let mut func = Function::new();
|
||||||
let ebb = dfg.make_ebb();
|
let ebb = func.dfg.make_ebb();
|
||||||
let arg32 = dfg.append_ebb_param(ebb, types::I32);
|
let arg32 = func.dfg.append_ebb_param(ebb, types::I32);
|
||||||
|
|
||||||
// Create an imul.i32 which is encodable in RV32M.
|
// Create an imul.i32 which is encodable in RV32M.
|
||||||
let mul32 = InstructionData::Binary {
|
let mul32 = InstructionData::Binary {
|
||||||
opcode: Opcode::Imul,
|
opcode: Opcode::Imul,
|
||||||
args: [arg32, arg32],
|
args: [arg32, arg32],
|
||||||
};
|
};
|
||||||
assert_eq!(encstr(&*isa, isa.encode(&dfg, &mul32, types::I32)), "R#10c");
|
assert_eq!(
|
||||||
|
encstr(&*isa, isa.encode(&func, &mul32, types::I32)),
|
||||||
|
"R#10c"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,14 +72,14 @@ impl TargetIsa for Isa {
|
|||||||
|
|
||||||
fn legal_encodings<'a>(
|
fn legal_encodings<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
dfg: &'a ir::DataFlowGraph,
|
func: &'a ir::Function,
|
||||||
inst: &'a ir::InstructionData,
|
inst: &'a ir::InstructionData,
|
||||||
ctrl_typevar: ir::Type,
|
ctrl_typevar: ir::Type,
|
||||||
) -> Encodings<'a> {
|
) -> Encodings<'a> {
|
||||||
lookup_enclist(
|
lookup_enclist(
|
||||||
ctrl_typevar,
|
ctrl_typevar,
|
||||||
inst,
|
inst,
|
||||||
dfg,
|
func,
|
||||||
self.cpumode,
|
self.cpumode,
|
||||||
&enc_tables::LEVEL2[..],
|
&enc_tables::LEVEL2[..],
|
||||||
&enc_tables::ENCLISTS[..],
|
&enc_tables::ENCLISTS[..],
|
||||||
|
|||||||
@@ -54,8 +54,10 @@ fn make_funcref(libcall: ir::LibCall, inst: ir::Inst, func: &mut ir::Function) -
|
|||||||
}
|
}
|
||||||
let sigref = func.import_signature(sig);
|
let sigref = func.import_signature(sig);
|
||||||
|
|
||||||
|
// TODO: Can libcalls be colocated in some circumstances?
|
||||||
func.import_function(ir::ExtFuncData {
|
func.import_function(ir::ExtFuncData {
|
||||||
name: ir::ExternalName::LibCall(libcall),
|
name: ir::ExternalName::LibCall(libcall),
|
||||||
signature: sigref,
|
signature: sigref,
|
||||||
|
colocated: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
//! Some of these predicates may be unused in certain ISA configurations, so we suppress the
|
//! Some of these predicates may be unused in certain ISA configurations, so we suppress the
|
||||||
//! dead code warning.
|
//! dead code warning.
|
||||||
|
|
||||||
|
use ir;
|
||||||
|
|
||||||
/// Check that `x` is the same as `y`.
|
/// Check that `x` is the same as `y`.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn is_equal<T: Eq + Copy, O: Into<T> + Copy>(x: T, y: O) -> bool {
|
pub fn is_equal<T: Eq + Copy, O: Into<T> + Copy>(x: T, y: O) -> bool {
|
||||||
@@ -31,6 +33,19 @@ pub fn is_unsigned_int<T: Into<i64>>(x: T, wd: u8, sc: u8) -> bool {
|
|||||||
u == (u & m)
|
u == (u & m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_colocated_func(func_ref: ir::FuncRef, func: &ir::Function) -> bool {
|
||||||
|
func.dfg.ext_funcs[func_ref].colocated
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn is_colocated_data(global_var: ir::GlobalVar, func: &ir::Function) -> bool {
|
||||||
|
match func.global_vars[global_var] {
|
||||||
|
ir::GlobalVarData::Sym { colocated, .. } => colocated,
|
||||||
|
_ => panic!("is_colocated_data only makes sense for data with symbolic addresses"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -1025,7 +1025,7 @@ impl<'a> Verifier<'a> {
|
|||||||
let encoding = self.func.encodings[inst];
|
let encoding = self.func.encodings[inst];
|
||||||
if encoding.is_legal() {
|
if encoding.is_legal() {
|
||||||
let mut encodings = isa.legal_encodings(
|
let mut encodings = isa.legal_encodings(
|
||||||
&self.func.dfg,
|
&self.func,
|
||||||
&self.func.dfg[inst],
|
&self.func.dfg[inst],
|
||||||
self.func.dfg.ctrl_typevar(inst),
|
self.func.dfg.ctrl_typevar(inst),
|
||||||
).peekable();
|
).peekable();
|
||||||
@@ -1045,7 +1045,7 @@ impl<'a> Verifier<'a> {
|
|||||||
let mut multiple_encodings = false;
|
let mut multiple_encodings = false;
|
||||||
|
|
||||||
for enc in isa.legal_encodings(
|
for enc in isa.legal_encodings(
|
||||||
&self.func.dfg,
|
&self.func,
|
||||||
&self.func.dfg[inst],
|
&self.func.dfg[inst],
|
||||||
self.func.dfg.ctrl_typevar(inst),
|
self.func.dfg.ctrl_typevar(inst),
|
||||||
)
|
)
|
||||||
@@ -1099,7 +1099,7 @@ impl<'a> Verifier<'a> {
|
|||||||
if let Some(text) = needs_enc {
|
if let Some(text) = needs_enc {
|
||||||
// This instruction needs an encoding, so generate an error.
|
// This instruction needs an encoding, so generate an error.
|
||||||
// Provide the ISA default encoding as a hint.
|
// Provide the ISA default encoding as a hint.
|
||||||
match self.func.dfg.encode(inst, isa) {
|
match self.func.encode(inst, isa) {
|
||||||
Ok(enc) => {
|
Ok(enc) => {
|
||||||
return err!(
|
return err!(
|
||||||
inst,
|
inst,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub fn write_function(w: &mut Write, func: &Function, isa: Option<&TargetIsa>) -
|
|||||||
let regs = isa.map(TargetIsa::register_info);
|
let regs = isa.map(TargetIsa::register_info);
|
||||||
let regs = regs.as_ref();
|
let regs = regs.as_ref();
|
||||||
|
|
||||||
|
write!(w, "function ")?;
|
||||||
write_spec(w, func, regs)?;
|
write_spec(w, func, regs)?;
|
||||||
writeln!(w, " {{")?;
|
writeln!(w, " {{")?;
|
||||||
let mut any = write_preamble(w, func, regs)?;
|
let mut any = write_preamble(w, func, regs)?;
|
||||||
@@ -34,7 +35,7 @@ pub fn write_function(w: &mut Write, func: &Function, isa: Option<&TargetIsa>) -
|
|||||||
// Function spec.
|
// Function spec.
|
||||||
|
|
||||||
fn write_spec(w: &mut Write, func: &Function, regs: Option<&RegInfo>) -> Result {
|
fn write_spec(w: &mut Write, func: &Function, regs: Option<&RegInfo>) -> Result {
|
||||||
write!(w, "function {}{}", func.name, func.signature.display(regs))
|
write!(w, "{}{}", func.name, func.signature.display(regs))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_preamble(
|
fn write_preamble(
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ impl SubTest for TestBinEmit {
|
|||||||
// constraints.
|
// constraints.
|
||||||
if let Some(enc) = {
|
if let Some(enc) = {
|
||||||
let mut legal_encodings = isa.legal_encodings(
|
let mut legal_encodings = isa.legal_encodings(
|
||||||
&func.dfg,
|
&func,
|
||||||
&func.dfg[inst],
|
&func.dfg[inst],
|
||||||
func.dfg.ctrl_typevar(inst),
|
func.dfg.ctrl_typevar(inst),
|
||||||
).filter(|e| {
|
).filter(|e| {
|
||||||
@@ -251,7 +251,7 @@ impl SubTest for TestBinEmit {
|
|||||||
|
|
||||||
// Do any encodings exist?
|
// Do any encodings exist?
|
||||||
let encodings = isa.legal_encodings(
|
let encodings = isa.legal_encodings(
|
||||||
&func.dfg,
|
&func,
|
||||||
&func.dfg[inst],
|
&func.dfg[inst],
|
||||||
func.dfg.ctrl_typevar(inst),
|
func.dfg.ctrl_typevar(inst),
|
||||||
).map(|e| encinfo.display(e))
|
).map(|e| encinfo.display(e))
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ impl<'a> Context<'a> {
|
|||||||
while self.function.global_vars.next_key().index() <= gv.index() {
|
while self.function.global_vars.next_key().index() <= gv.index() {
|
||||||
self.function.create_global_var(GlobalVarData::Sym {
|
self.function.create_global_var(GlobalVarData::Sym {
|
||||||
name: ExternalName::testcase(""),
|
name: ExternalName::testcase(""),
|
||||||
|
colocated: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.function.global_vars[gv] = data;
|
self.function.global_vars[gv] = data;
|
||||||
@@ -208,6 +209,7 @@ impl<'a> Context<'a> {
|
|||||||
self.function.import_function(ExtFuncData {
|
self.function.import_function(ExtFuncData {
|
||||||
name: ExternalName::testcase(""),
|
name: ExternalName::testcase(""),
|
||||||
signature: SigRef::reserved_value(),
|
signature: SigRef::reserved_value(),
|
||||||
|
colocated: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.function.dfg.ext_funcs[fn_] = data;
|
self.function.dfg.ext_funcs[fn_] = data;
|
||||||
@@ -760,7 +762,7 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
// Parse a whole function definition.
|
// Parse a whole function definition.
|
||||||
//
|
//
|
||||||
// function ::= * function-spec "{" preamble function-body "}"
|
// function ::= * "function" name signature "{" preamble function-body "}"
|
||||||
//
|
//
|
||||||
fn parse_function(
|
fn parse_function(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -772,10 +774,19 @@ impl<'a> Parser<'a> {
|
|||||||
debug_assert!(self.comments.is_empty());
|
debug_assert!(self.comments.is_empty());
|
||||||
self.start_gathering_comments();
|
self.start_gathering_comments();
|
||||||
|
|
||||||
let (location, name, sig) = self.parse_function_spec(unique_isa)?;
|
self.match_identifier("function", "expected 'function'")?;
|
||||||
|
|
||||||
|
let location = self.loc;
|
||||||
|
|
||||||
|
// function ::= "function" * name signature "{" preamble function-body "}"
|
||||||
|
let name = self.parse_external_name()?;
|
||||||
|
|
||||||
|
// function ::= "function" name * signature "{" preamble function-body "}"
|
||||||
|
let sig = self.parse_signature(unique_isa)?;
|
||||||
|
|
||||||
let mut ctx = Context::new(Function::with_name_signature(name, sig), unique_isa);
|
let mut ctx = Context::new(Function::with_name_signature(name, sig), unique_isa);
|
||||||
|
|
||||||
// function ::= function-spec * "{" preamble function-body "}"
|
// function ::= "function" name signature * "{" preamble function-body "}"
|
||||||
self.match_token(
|
self.match_token(
|
||||||
Token::LBrace,
|
Token::LBrace,
|
||||||
"expected '{' before function body",
|
"expected '{' before function body",
|
||||||
@@ -784,11 +795,11 @@ impl<'a> Parser<'a> {
|
|||||||
self.token();
|
self.token();
|
||||||
self.claim_gathered_comments(AnyEntity::Function);
|
self.claim_gathered_comments(AnyEntity::Function);
|
||||||
|
|
||||||
// function ::= function-spec "{" * preamble function-body "}"
|
// function ::= "function" name signature "{" * preamble function-body "}"
|
||||||
self.parse_preamble(&mut ctx)?;
|
self.parse_preamble(&mut ctx)?;
|
||||||
// function ::= function-spec "{" preamble * function-body "}"
|
// function ::= "function" name signature "{" preamble * function-body "}"
|
||||||
self.parse_function_body(&mut ctx)?;
|
self.parse_function_body(&mut ctx)?;
|
||||||
// function ::= function-spec "{" preamble function-body * "}"
|
// function ::= "function" name signature "{" preamble function-body * "}"
|
||||||
self.match_token(
|
self.match_token(
|
||||||
Token::RBrace,
|
Token::RBrace,
|
||||||
"expected '}' after function body",
|
"expected '}' after function body",
|
||||||
@@ -808,29 +819,9 @@ impl<'a> Parser<'a> {
|
|||||||
Ok((ctx.function, details))
|
Ok((ctx.function, details))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a function spec.
|
|
||||||
//
|
|
||||||
// function-spec ::= * "function" name signature
|
|
||||||
//
|
|
||||||
fn parse_function_spec(
|
|
||||||
&mut self,
|
|
||||||
unique_isa: Option<&TargetIsa>,
|
|
||||||
) -> Result<(Location, ExternalName, Signature)> {
|
|
||||||
self.match_identifier("function", "expected 'function'")?;
|
|
||||||
let location = self.loc;
|
|
||||||
|
|
||||||
// function-spec ::= "function" * name signature
|
|
||||||
let name = self.parse_external_name()?;
|
|
||||||
|
|
||||||
// function-spec ::= "function" name * signature
|
|
||||||
let sig = self.parse_signature(unique_isa)?;
|
|
||||||
|
|
||||||
Ok((location, name, sig))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse an external name.
|
// Parse an external name.
|
||||||
//
|
//
|
||||||
// For example, in a function spec, the parser would be in this state:
|
// For example, in a function decl, the parser would be in this state:
|
||||||
//
|
//
|
||||||
// function ::= "function" * name signature { ... }
|
// function ::= "function" * name signature { ... }
|
||||||
//
|
//
|
||||||
@@ -1095,7 +1086,7 @@ impl<'a> Parser<'a> {
|
|||||||
// global-var-decl ::= * GlobalVar(gv) "=" global-var-desc
|
// global-var-decl ::= * GlobalVar(gv) "=" global-var-desc
|
||||||
// global-var-desc ::= "vmctx" offset32
|
// global-var-desc ::= "vmctx" offset32
|
||||||
// | "deref" "(" GlobalVar(base) ")" offset32
|
// | "deref" "(" GlobalVar(base) ")" offset32
|
||||||
// | "globalsym" name
|
// | globalsym ["colocated"] name
|
||||||
//
|
//
|
||||||
fn parse_global_var_decl(&mut self) -> Result<(GlobalVar, GlobalVarData)> {
|
fn parse_global_var_decl(&mut self) -> Result<(GlobalVar, GlobalVarData)> {
|
||||||
let gv = self.match_gv("expected global variable number: gv«n»")?;
|
let gv = self.match_gv("expected global variable number: gv«n»")?;
|
||||||
@@ -1124,8 +1115,9 @@ impl<'a> Parser<'a> {
|
|||||||
GlobalVarData::Deref { base, offset }
|
GlobalVarData::Deref { base, offset }
|
||||||
}
|
}
|
||||||
"globalsym" => {
|
"globalsym" => {
|
||||||
|
let colocated = self.optional(Token::Identifier("colocated"));
|
||||||
let name = self.parse_external_name()?;
|
let name = self.parse_external_name()?;
|
||||||
GlobalVarData::Sym { name }
|
GlobalVarData::Sym { name, colocated }
|
||||||
}
|
}
|
||||||
other => return err!(self.loc, "Unknown global variable kind '{}'", other),
|
other => return err!(self.loc, "Unknown global variable kind '{}'", other),
|
||||||
};
|
};
|
||||||
@@ -1237,8 +1229,8 @@ impl<'a> Parser<'a> {
|
|||||||
//
|
//
|
||||||
// Two variants:
|
// Two variants:
|
||||||
//
|
//
|
||||||
// function-decl ::= FuncRef(fnref) "=" function-spec
|
// function-decl ::= FuncRef(fnref) "=" ["colocated"]" name function-decl-sig
|
||||||
// FuncRef(fnref) "=" SigRef(sig) name
|
// function-decl-sig ::= SigRef(sig) | signature
|
||||||
//
|
//
|
||||||
// The first variant allocates a new signature reference. The second references an existing
|
// The first variant allocates a new signature reference. The second references an existing
|
||||||
// signature which must be declared first.
|
// signature which must be declared first.
|
||||||
@@ -1250,9 +1242,19 @@ impl<'a> Parser<'a> {
|
|||||||
"expected '=' in function decl",
|
"expected '=' in function decl",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let loc = self.loc;
|
||||||
|
|
||||||
|
// function-decl ::= FuncRef(fnref) "=" * ["colocated"] name function-decl-sig
|
||||||
|
let colocated = self.optional(Token::Identifier("colocated"));
|
||||||
|
|
||||||
|
// function-decl ::= FuncRef(fnref) "=" ["colocated"] * name function-decl-sig
|
||||||
|
let name = self.parse_external_name()?;
|
||||||
|
|
||||||
|
// function-decl ::= FuncRef(fnref) "=" ["colocated"] name * function-decl-sig
|
||||||
let data = match self.token() {
|
let data = match self.token() {
|
||||||
Some(Token::Identifier("function")) => {
|
Some(Token::LPar) => {
|
||||||
let (loc, name, sig) = self.parse_function_spec(ctx.unique_isa)?;
|
// function-decl ::= FuncRef(fnref) "=" ["colocated"] name * signature
|
||||||
|
let sig = self.parse_signature(ctx.unique_isa)?;
|
||||||
let sigref = ctx.function.import_signature(sig);
|
let sigref = ctx.function.import_signature(sig);
|
||||||
ctx.map.def_entity(sigref.into(), &loc).expect(
|
ctx.map.def_entity(sigref.into(), &loc).expect(
|
||||||
"duplicate SigRef entities created",
|
"duplicate SigRef entities created",
|
||||||
@@ -1260,6 +1262,7 @@ impl<'a> Parser<'a> {
|
|||||||
ExtFuncData {
|
ExtFuncData {
|
||||||
name,
|
name,
|
||||||
signature: sigref,
|
signature: sigref,
|
||||||
|
colocated,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Token::SigRef(sig_src)) => {
|
Some(Token::SigRef(sig_src)) => {
|
||||||
@@ -1271,10 +1274,10 @@ impl<'a> Parser<'a> {
|
|||||||
};
|
};
|
||||||
ctx.check_sig(sig, &self.loc)?;
|
ctx.check_sig(sig, &self.loc)?;
|
||||||
self.consume();
|
self.consume();
|
||||||
let name = self.parse_external_name()?;
|
|
||||||
ExtFuncData {
|
ExtFuncData {
|
||||||
name,
|
name,
|
||||||
signature: sig,
|
signature: sig,
|
||||||
|
colocated,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => return err!(self.loc, "expected 'function' or sig«n» in function decl"),
|
_ => return err!(self.loc, "expected 'function' or sig«n» in function decl"),
|
||||||
|
|||||||
@@ -182,7 +182,11 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
|||||||
// And maybe attempt some signature de-duplication.
|
// And maybe attempt some signature de-duplication.
|
||||||
let signature = func.import_signature(self.vmctx_sig(sigidx));
|
let signature = func.import_signature(self.vmctx_sig(sigidx));
|
||||||
let name = get_func_name(index);
|
let name = get_func_name(index);
|
||||||
func.import_function(ir::ExtFuncData { name, signature })
|
func.import_function(ir::ExtFuncData {
|
||||||
|
name,
|
||||||
|
signature,
|
||||||
|
colocated: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_call_indirect(
|
fn translate_call_indirect(
|
||||||
|
|||||||
Reference in New Issue
Block a user