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
|
||||
|
||||
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):
|
||||
brz v2, ebb2
|
||||
|
||||
@@ -48,8 +48,7 @@ A ``.cton`` file consists of a sequence of independent function definitions:
|
||||
|
||||
.. productionlist::
|
||||
function_list : { function }
|
||||
function : function_spec "{" preamble function_body "}"
|
||||
function_spec : "function" function_name signature
|
||||
function : "function" function_name signature "{" preamble function_body "}"
|
||||
preamble : { preamble_decl }
|
||||
function_body : { extended_basic_block }
|
||||
|
||||
@@ -409,10 +408,14 @@ compilers.
|
||||
Functions that are called directly must be declared in the :term:`function
|
||||
preamble`:
|
||||
|
||||
.. inst:: FN = function NAME signature
|
||||
.. inst:: FN = [colocated] NAME signature
|
||||
|
||||
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 signature: Function signature. See below.
|
||||
:result FN: A function identifier that can be used with :inst:`call`.
|
||||
@@ -570,13 +573,17 @@ runtime data structures.
|
||||
variable.
|
||||
:result GV: Global variable.
|
||||
|
||||
.. inst:: GV = globalsym name
|
||||
.. inst:: GV = [colocated] globalsym name
|
||||
|
||||
Declare a global variable at a symbolic address.
|
||||
|
||||
The address of GV is symbolic and will be assigned a relocation, so that
|
||||
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.
|
||||
:result GV: Global variable.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ isa riscv
|
||||
|
||||
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
||||
sig0 = ()
|
||||
fn0 = function %foo()
|
||||
fn0 = %foo()
|
||||
|
||||
ebb0(v9999: i32):
|
||||
[-,%x10] v1 = iconst.i32 1
|
||||
|
||||
@@ -17,8 +17,8 @@ ebb0(v0: i64):
|
||||
}
|
||||
|
||||
function %split_call_arg(i32) {
|
||||
fn1 = function %foo(i64)
|
||||
fn2 = function %foo(i32, i64)
|
||||
fn1 = %foo(i64)
|
||||
fn2 = %foo(i32, i64)
|
||||
ebb0(v0: i32):
|
||||
v1 = uextend.i64 v0
|
||||
call fn1(v1)
|
||||
@@ -30,7 +30,7 @@ ebb0(v0: i32):
|
||||
}
|
||||
|
||||
function %split_ret_val() {
|
||||
fn1 = function %foo() -> i64
|
||||
fn1 = %foo() -> i64
|
||||
ebb0:
|
||||
v1 = call fn1()
|
||||
; check: ebb0($(link=$V): i32):
|
||||
@@ -45,7 +45,7 @@ ebb1(v10: i64):
|
||||
|
||||
; First return value is fine, second one is expanded.
|
||||
function %split_ret_val2() {
|
||||
fn1 = function %foo() -> i32, i64
|
||||
fn1 = %foo() -> i32, i64
|
||||
ebb0:
|
||||
v1, v2 = call fn1()
|
||||
; 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 %ext_ret_val() {
|
||||
fn1 = function %foo() -> i8 sext
|
||||
fn1 = %foo() -> i8 sext
|
||||
ebb0:
|
||||
v1 = call fn1()
|
||||
; check: ebb0($V: i32):
|
||||
@@ -124,7 +124,7 @@ ebb0(v0: i32, v1: f32x2):
|
||||
; Call a function that takes arguments on the stack.
|
||||
function %stack_args(i32) {
|
||||
; check: $(ss0=$SS) = outgoing_arg 4
|
||||
fn1 = function %foo(i64, i64, i64, i64, i32)
|
||||
fn1 = %foo(i64, i64, i64, i64, i32)
|
||||
ebb0(v0: i32):
|
||||
v1 = iconst.i64 1
|
||||
call fn1(v1, v1, v1, v1, v0)
|
||||
|
||||
@@ -27,9 +27,9 @@ function %parse_encoding(i32 [%x5]) -> i32 [%x10] {
|
||||
; check: sig5 = () -> f32 [0] system_v
|
||||
|
||||
; 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
|
||||
; nextln: fn0 = sig6 %bar
|
||||
; nextln: fn0 = %bar sig6
|
||||
|
||||
ebb0(v0: i32):
|
||||
return v0
|
||||
|
||||
@@ -2,7 +2,7 @@ test verifier
|
||||
isa riscv
|
||||
|
||||
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
||||
fn0 = function %foo()
|
||||
fn0 = %foo()
|
||||
|
||||
ebb0(v9999: i32):
|
||||
; iconst.i32 needs legalizing, so it should throw a
|
||||
@@ -11,7 +11,7 @@ ebb0(v9999: i32):
|
||||
}
|
||||
|
||||
function %RV32I(i32 link [%x1]) -> i32 link [%x1] {
|
||||
fn0 = function %foo()
|
||||
fn0 = %foo()
|
||||
|
||||
ebb0(v9999: i32):
|
||||
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 {
|
||||
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):
|
||||
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.
|
||||
function %I32() {
|
||||
sig0 = ()
|
||||
fn0 = function %foo()
|
||||
fn0 = %foo()
|
||||
|
||||
ebb0:
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ isa x86 haswell
|
||||
; Tests from binary64.cton affected by allones_funcaddrs.
|
||||
function %I64() {
|
||||
sig0 = ()
|
||||
fn0 = function %foo()
|
||||
fn0 = %foo()
|
||||
|
||||
ebb0:
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ isa x86 haswell
|
||||
|
||||
function %I32() {
|
||||
sig0 = ()
|
||||
fn0 = function %foo()
|
||||
fn0 = %foo()
|
||||
|
||||
gv0 = globalsym %some_gv
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@ isa x86 haswell
|
||||
; Tests for i64 instructions.
|
||||
function %I64() {
|
||||
sig0 = ()
|
||||
fn0 = function %foo()
|
||||
fn0 = %foo()
|
||||
fn1 = colocated %bar()
|
||||
|
||||
gv0 = globalsym %some_gv
|
||||
gv1 = globalsym colocated %some_gv
|
||||
|
||||
; Use incoming_arg stack slots because they won't be relocated by the frame
|
||||
; layout.
|
||||
@@ -29,6 +31,9 @@ ebb0:
|
||||
; asm: call foo@PLT
|
||||
call fn0() ; bin: e8 PLTRel4(%foo-4) 00000000
|
||||
|
||||
; asm: call foo
|
||||
call fn1() ; bin: e8 PCRel4(%bar-4) 00000000
|
||||
|
||||
; asm: mov 0x0(%rip), %rax
|
||||
[-,%rax] v0 = func_addr.i64 fn0 ; bin: 48 8b 05 GOTPCRel4(%foo-4) 00000000
|
||||
; asm: mov 0x0(%rip), %rsi
|
||||
@@ -50,5 +55,12 @@ ebb0:
|
||||
; asm: mov 0x0(%rip), %r10
|
||||
[-,%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
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ isa x86 haswell
|
||||
; Tests for i64 instructions.
|
||||
function %I64() {
|
||||
sig0 = ()
|
||||
fn0 = function %foo()
|
||||
fn0 = %foo()
|
||||
|
||||
gv0 = globalsym %some_gv
|
||||
|
||||
@@ -473,12 +473,6 @@ ebb0:
|
||||
; asm: movzbq %dl, %rsi
|
||||
[-,%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
|
||||
[-,%rcx] v400 = func_addr.i64 fn0 ; bin: 48 b9 Abs8(%foo) 0000000000000000
|
||||
; asm: movabsq $0, %rsi
|
||||
@@ -713,7 +707,7 @@ ebb0:
|
||||
; be done by an instruction shrinking pass.
|
||||
function %I32() {
|
||||
sig0 = ()
|
||||
fn0 = function %foo()
|
||||
fn0 = %foo()
|
||||
|
||||
ss0 = incoming_arg 8, offset 0
|
||||
ss1 = incoming_arg 1024, offset -1024
|
||||
|
||||
@@ -12,5 +12,5 @@ ebb0(v0: f32):
|
||||
}
|
||||
; check: function %floor(f32 [%xmm0]) -> f32 [%xmm0] system_v {
|
||||
; check: sig0 = (f32) -> f32 system_v
|
||||
; check: fn0 = sig0 %FloorF32
|
||||
; check: fn0 = %FloorF32 sig0
|
||||
; check: v1 = call fn0(v0)
|
||||
|
||||
@@ -45,7 +45,7 @@ ebb0:
|
||||
; A function performing a call.
|
||||
|
||||
function %call() {
|
||||
fn0 = function %foo()
|
||||
fn0 = %foo()
|
||||
|
||||
ebb0:
|
||||
call fn0()
|
||||
@@ -55,7 +55,7 @@ ebb0:
|
||||
; check: function %call(i64 fp [%rbp]) -> i64 fp [%rbp] system_v {
|
||||
; nextln: ss0 = incoming_arg 16, offset -16
|
||||
; nextln: sig0 = () system_v
|
||||
; nextln: fn0 = sig0 %foo
|
||||
; nextln: fn0 = %foo sig0
|
||||
; nextln:
|
||||
; nextln: ebb0(v0: i64 [%rbp]):
|
||||
; nextln: x86_push v0
|
||||
|
||||
@@ -26,22 +26,22 @@ ebb1:
|
||||
function %signatures() {
|
||||
sig10 = ()
|
||||
sig11 = (i32, f64) -> i32, b1 spiderwasm
|
||||
fn5 = sig11 %foo
|
||||
fn8 = function %bar(i32) -> b1
|
||||
fn5 = %foo sig11
|
||||
fn8 = %bar(i32) -> b1
|
||||
}
|
||||
; sameln: function %signatures() system_v {
|
||||
; check: sig10 = () system_v
|
||||
; check: sig11 = (i32, f64) -> i32, b1 spiderwasm
|
||||
; check: sig12 = (i32) -> b1 system_v
|
||||
; not: fn0
|
||||
; check: fn5 = sig11 %foo
|
||||
; check: fn8 = sig12 %bar
|
||||
; check: fn5 = %foo sig11
|
||||
; check: fn8 = %bar sig12
|
||||
; check: }
|
||||
|
||||
function %direct() {
|
||||
fn0 = function %none()
|
||||
fn1 = function %one() -> i32
|
||||
fn2 = function %two() -> i32, f32
|
||||
fn0 = %none()
|
||||
fn1 = %one() -> i32
|
||||
fn2 = %two() -> i32, f32
|
||||
|
||||
ebb0:
|
||||
call fn0()
|
||||
@@ -72,7 +72,7 @@ ebb0(v0: i64):
|
||||
|
||||
function %long_call() {
|
||||
sig0 = ()
|
||||
fn0 = sig0 %none
|
||||
fn0 = %none sig0
|
||||
|
||||
ebb0:
|
||||
v0 = func_addr.i32 fn0
|
||||
|
||||
@@ -53,7 +53,7 @@ ebb1(v10: i32):
|
||||
|
||||
; Pass an EBB argument as a function argument.
|
||||
function %callebb(i32, i32) -> i32 {
|
||||
fn0 = function %foo(i32) -> i32
|
||||
fn0 = %foo(i32) -> i32
|
||||
|
||||
ebb0(v1: i32, v2: i32):
|
||||
brnz v1, ebb1(v1)
|
||||
@@ -66,7 +66,7 @@ ebb1(v10: i32):
|
||||
|
||||
; Pass an EBB argument as a jump argument.
|
||||
function %jumpebb(i32, i32) -> i32 {
|
||||
fn0 = function %foo(i32) -> i32
|
||||
fn0 = %foo(i32) -> i32
|
||||
|
||||
ebb0(v1: i32, v2: i32):
|
||||
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
|
||||
sig1 = (i64 vmctx, i32, i32, i32) -> i32 system_v
|
||||
sig2 = (i64 vmctx, i32, i32, i32) -> i32 system_v
|
||||
fn0 = sig0 u0:2
|
||||
fn1 = sig1 u0:0
|
||||
fn2 = sig2 u0:1
|
||||
fn0 = u0:2 sig0
|
||||
fn1 = u0:0 sig1
|
||||
fn2 = u0:1 sig2
|
||||
|
||||
ebb0(v0: i64, v1: i32, v2: i32):
|
||||
v3 = iconst.i32 0
|
||||
@@ -1038,7 +1038,7 @@ function %musl(f64 [%xmm0], i64 vmctx [%rdi]) -> f64 [%xmm0] system_v {
|
||||
gv0 = vmctx
|
||||
heap0 = static gv0, min 0, bound 0x0001_0000_0000, guard 0x8000_0000
|
||||
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):
|
||||
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
|
||||
sig0 = (i64 vmctx [%rdi]) -> i32 [%rax] system_v
|
||||
sig1 = (i64 vmctx [%rdi], i32 [%rsi]) system_v
|
||||
fn0 = sig0 u0:1
|
||||
fn1 = sig1 u0:3
|
||||
fn0 = u0:1 sig0
|
||||
fn1 = u0:3 sig1
|
||||
|
||||
ebb0(v0: i64):
|
||||
v1 = iconst.i32 0
|
||||
|
||||
@@ -5,7 +5,7 @@ isa riscv enable_e
|
||||
|
||||
; Check that we can handle a function return value that got spilled.
|
||||
function %spill_return() -> i32 {
|
||||
fn0 = function %foo() -> i32 system_v
|
||||
fn0 = %foo() -> i32 system_v
|
||||
|
||||
ebb0:
|
||||
v0 = call fn0()
|
||||
|
||||
@@ -70,7 +70,7 @@ ebb0(v1: i32):
|
||||
|
||||
; All values live across a call must be spilled
|
||||
function %across_call(i32) {
|
||||
fn0 = function %foo(i32)
|
||||
fn0 = %foo(i32)
|
||||
ebb0(v1: i32):
|
||||
; check: v1 = spill
|
||||
call fn0(v1)
|
||||
@@ -83,7 +83,7 @@ ebb0(v1: i32):
|
||||
|
||||
; The same value used for two function arguments.
|
||||
function %doubleuse(i32) {
|
||||
fn0 = function %xx(i32, i32)
|
||||
fn0 = %xx(i32, i32)
|
||||
ebb0(v0: i32):
|
||||
; check: $(c=$V) = copy v0
|
||||
call fn0(v0, v0)
|
||||
|
||||
@@ -42,14 +42,14 @@ function %type_mismatch_controlling_variable() {
|
||||
}
|
||||
|
||||
function %fn_call_too_few_args() {
|
||||
fn2 = function %great_fn(i32, f32)
|
||||
fn2 = %great_fn(i32, f32)
|
||||
ebb0:
|
||||
call fn2() ; error: mismatched argument count, got 0, expected 2
|
||||
return
|
||||
}
|
||||
|
||||
function %fn_call_too_many_args() {
|
||||
fn5 = function %best_fn()
|
||||
fn5 = %best_fn()
|
||||
ebb0:
|
||||
v0 = iconst.i64 56
|
||||
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.
|
||||
|
||||
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())
|
||||
|
||||
|
||||
@@ -409,7 +409,7 @@ class CtrlTypePredicate(object):
|
||||
"""
|
||||
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())
|
||||
|
||||
@@ -74,7 +74,7 @@ except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def emit_instp(instp, fmt, has_dfg=False):
|
||||
def emit_instp(instp, fmt, has_func=False):
|
||||
# type: (PredNode, srcgen.Formatter, bool) -> None
|
||||
"""
|
||||
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.
|
||||
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))
|
||||
return
|
||||
|
||||
@@ -114,11 +114,11 @@ def emit_instp(instp, fmt, has_dfg=False):
|
||||
.format(iform.name, fields), '}'):
|
||||
if has_type_check:
|
||||
# We could implement this if we need to.
|
||||
assert has_dfg, "Recipe predicates can't check type variables."
|
||||
fmt.line('let args = inst.arguments(&dfg.value_lists);')
|
||||
elif has_dfg:
|
||||
assert has_func, "Recipe predicates can't check type variables."
|
||||
fmt.line('let args = inst.arguments(&func.dfg.value_lists);')
|
||||
elif has_func:
|
||||
# Silence dead argument warning.
|
||||
fmt.line('let _ = dfg;')
|
||||
fmt.line('let _ = func;')
|
||||
fmt.format('return {};', instp.rust_predicate(0))
|
||||
fmt.line('unreachable!();')
|
||||
|
||||
@@ -132,9 +132,9 @@ def emit_inst_predicates(instps, fmt):
|
||||
for instp, number in instps.items():
|
||||
name = 'inst_predicate_{}'.format(number)
|
||||
with fmt.indented(
|
||||
'fn {}(dfg: &ir::DataFlowGraph, inst: &ir::InstructionData)'
|
||||
'fn {}(func: &ir::Function, inst: &ir::InstructionData)'
|
||||
'-> bool {{'.format(name), '}'):
|
||||
emit_instp(instp, fmt, has_dfg=True)
|
||||
emit_instp(instp, fmt, has_func=True)
|
||||
|
||||
# Generate the static table.
|
||||
with fmt.indented(
|
||||
|
||||
@@ -185,9 +185,9 @@ def unwrap_inst(iref, node, fmt):
|
||||
fmt.line('ref args,')
|
||||
fmt.line('..')
|
||||
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:
|
||||
fmt.line('let args = args.as_slice(&dfg.value_lists);')
|
||||
fmt.line('let args = args.as_slice(&func.dfg.value_lists);')
|
||||
elif nvops == 1:
|
||||
fmt.line('let args = [arg];')
|
||||
# Generate the values for the tuple.
|
||||
@@ -198,7 +198,7 @@ def unwrap_inst(iref, node, fmt):
|
||||
fmt.format('{},', iform.imm_fields[n].member)
|
||||
elif op.is_value():
|
||||
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.
|
||||
instp = expr.inst_predicate_with_ctrl_typevar()
|
||||
fmt.line(instp.rust_predicate(0) if instp else 'true')
|
||||
|
||||
@@ -3,8 +3,9 @@ x86 Encodings.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from cdsl.predicates import IsUnsignedInt, Not, And
|
||||
from base.predicates import IsColocatedFunc, IsColocatedData
|
||||
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 . import recipes as r
|
||||
from . import settings as cfg
|
||||
@@ -292,16 +293,22 @@ enc_both(base.regspill.f64, r.fregspill32, 0xf2, 0x0f, 0x11)
|
||||
# Function addresses.
|
||||
#
|
||||
|
||||
# Non-PIC, all-ones funcaddresses.
|
||||
X86_32.enc(base.func_addr.i32, *r.fnaddr4(0xb8),
|
||||
isap=And(Not(allones_funcaddrs), Not(is_pic)))
|
||||
X86_64.enc(base.func_addr.i64, *r.fnaddr8.rex(0xb8, w=1),
|
||||
isap=And(Not(allones_funcaddrs), Not(is_pic)))
|
||||
|
||||
# Non-PIC, all-zeros funcaddresses.
|
||||
X86_32.enc(base.func_addr.i32, *r.allones_fnaddr4(0xb8),
|
||||
isap=And(allones_funcaddrs, Not(is_pic)))
|
||||
X86_64.enc(base.func_addr.i64, *r.allones_fnaddr8.rex(0xb8, w=1),
|
||||
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),
|
||||
isap=is_pic)
|
||||
|
||||
@@ -309,18 +316,34 @@ X86_64.enc(base.func_addr.i64, *r.got_fnaddr8.rex(0x8b, w=1),
|
||||
# Global addresses.
|
||||
#
|
||||
|
||||
# Non-PIC
|
||||
X86_32.enc(base.globalsym_addr.i32, *r.gvaddr4(0xb8),
|
||||
isap=Not(is_pic))
|
||||
X86_64.enc(base.globalsym_addr.i64, *r.gvaddr8.rex(0xb8, w=1),
|
||||
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),
|
||||
isap=is_pic)
|
||||
|
||||
#
|
||||
# Call/return
|
||||
#
|
||||
|
||||
# 32-bit, both PIC and non-PIC.
|
||||
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_32.enc(base.call_indirect.i32, *r.call_r(0xff, rrr=2))
|
||||
|
||||
@@ -631,6 +631,21 @@ allones_fnaddr8 = TailRecipe(
|
||||
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', FuncAddr, size=5, ins=(), outs=GPR,
|
||||
# rex2 gets passed 0 for r/m register because the upper bit of
|
||||
@@ -669,6 +684,20 @@ gvaddr8 = TailRecipe(
|
||||
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.
|
||||
got_gvaddr8 = TailRecipe(
|
||||
'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.
|
||||
let dfg = &cur.func.dfg;
|
||||
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| {
|
||||
let range = encinfo.branch_range(enc).expect("Branch with no range");
|
||||
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?
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(match_wild_err_arm))]
|
||||
match self.isa.encode(
|
||||
&self.func.dfg,
|
||||
&self.func,
|
||||
&self.func.dfg[inst],
|
||||
ctrl_typevar,
|
||||
) {
|
||||
|
||||
@@ -7,7 +7,7 @@ use ir::extfunc::ExtFuncData;
|
||||
use ir::instructions::{BranchInfo, CallInfo, InstructionData};
|
||||
use ir::types;
|
||||
use ir::{Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueList, ValueListPool};
|
||||
use isa::{Encoding, Legalize, TargetIsa};
|
||||
use isa::TargetIsa;
|
||||
use packed_option::ReservedValue;
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
@@ -660,12 +660,6 @@ impl DataFlowGraph {
|
||||
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.
|
||||
|
||||
@@ -327,11 +327,18 @@ pub struct ExtFuncData {
|
||||
pub name: ExternalName,
|
||||
/// Call signature of function.
|
||||
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 {
|
||||
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,
|
||||
JumpTableData, SigRef, StackSlot, StackSlotData};
|
||||
use ir::{EbbOffsets, InstEncodings, JumpTables, SourceLocs, StackSlots, ValueLocations};
|
||||
use isa::{EncInfo, Legalize, TargetIsa};
|
||||
use isa::{EncInfo, Legalize, TargetIsa, Encoding};
|
||||
use std::fmt;
|
||||
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> {
|
||||
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 {
|
||||
/// The symbolic name.
|
||||
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.
|
||||
pub fn symbol_name(&self) -> &ExternalName {
|
||||
match *self {
|
||||
GlobalVarData::Sym { ref name } => name,
|
||||
GlobalVarData::Sym { ref name, .. } => name,
|
||||
_ => panic!("only symbols have names"),
|
||||
}
|
||||
}
|
||||
@@ -51,7 +56,15 @@ impl fmt::Display for GlobalVarData {
|
||||
match *self {
|
||||
GlobalVarData::VmCtx { offset } => write!(f, "vmctx{}", 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>(
|
||||
&'a self,
|
||||
dfg: &'a ir::DataFlowGraph,
|
||||
func: &'a ir::Function,
|
||||
inst: &'a ir::InstructionData,
|
||||
ctrl_typevar: ir::Type,
|
||||
) -> Encodings<'a> {
|
||||
lookup_enclist(
|
||||
ctrl_typevar,
|
||||
inst,
|
||||
dfg,
|
||||
func,
|
||||
self.cpumode,
|
||||
&enc_tables::LEVEL2[..],
|
||||
&enc_tables::ENCLISTS[..],
|
||||
|
||||
@@ -59,14 +59,14 @@ impl TargetIsa for Isa {
|
||||
|
||||
fn legal_encodings<'a>(
|
||||
&'a self,
|
||||
dfg: &'a ir::DataFlowGraph,
|
||||
func: &'a ir::Function,
|
||||
inst: &'a ir::InstructionData,
|
||||
ctrl_typevar: ir::Type,
|
||||
) -> Encodings<'a> {
|
||||
lookup_enclist(
|
||||
ctrl_typevar,
|
||||
inst,
|
||||
dfg,
|
||||
func,
|
||||
&enc_tables::LEVEL1_A64[..],
|
||||
&enc_tables::LEVEL2[..],
|
||||
&enc_tables::ENCLISTS[..],
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! `lib/cretonne/meta/gen_encoding.py`.
|
||||
|
||||
use constant_hash::{probe, Table};
|
||||
use ir::{DataFlowGraph, InstructionData, Opcode, Type};
|
||||
use ir::{Function, InstructionData, Opcode, Type};
|
||||
use isa::{Encoding, Legalize};
|
||||
use settings::PredicateView;
|
||||
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
|
||||
/// 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.
|
||||
///
|
||||
@@ -106,7 +106,7 @@ impl<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
|
||||
pub fn lookup_enclist<'a, OffT1, OffT2>(
|
||||
ctrl_typevar: Type,
|
||||
inst: &'a InstructionData,
|
||||
dfg: &'a DataFlowGraph,
|
||||
func: &'a Function,
|
||||
level1_table: &'static [Level1Entry<OffT1>],
|
||||
level2_table: &'static [Level2Entry<OffT2>],
|
||||
enclist: &'static [EncListEntry],
|
||||
@@ -150,7 +150,7 @@ where
|
||||
offset,
|
||||
legalize,
|
||||
inst,
|
||||
dfg,
|
||||
func,
|
||||
enclist,
|
||||
legalize_actions,
|
||||
recipe_preds,
|
||||
@@ -177,7 +177,7 @@ pub struct Encodings<'a> {
|
||||
// Legalization code to use of no encoding is found.
|
||||
legalize: LegalizeCode,
|
||||
inst: &'a InstructionData,
|
||||
dfg: &'a DataFlowGraph,
|
||||
func: &'a Function,
|
||||
enclist: &'static [EncListEntry],
|
||||
legalize_actions: &'static [Legalize],
|
||||
recipe_preds: &'static [RecipePredicate],
|
||||
@@ -195,7 +195,7 @@ impl<'a> Encodings<'a> {
|
||||
offset: usize,
|
||||
legalize: LegalizeCode,
|
||||
inst: &'a InstructionData,
|
||||
dfg: &'a DataFlowGraph,
|
||||
func: &'a Function,
|
||||
enclist: &'static [EncListEntry],
|
||||
legalize_actions: &'static [Legalize],
|
||||
recipe_preds: &'static [RecipePredicate],
|
||||
@@ -205,7 +205,7 @@ impl<'a> Encodings<'a> {
|
||||
Encodings {
|
||||
offset,
|
||||
inst,
|
||||
dfg,
|
||||
func,
|
||||
legalize,
|
||||
isa_preds,
|
||||
recipe_preds,
|
||||
@@ -236,7 +236,7 @@ impl<'a> Encodings<'a> {
|
||||
/// Check an instruction or isa predicate.
|
||||
fn check_pred(&self, pred: usize) -> bool {
|
||||
if let Some(&p) = self.inst_preds.get(pred) {
|
||||
p(self.dfg, self.inst)
|
||||
p(self.func, self.inst)
|
||||
} else {
|
||||
let pred = pred - self.inst_preds.len();
|
||||
self.isa_preds.test(pred)
|
||||
|
||||
@@ -167,7 +167,7 @@ pub trait TargetIsa: fmt::Display {
|
||||
/// Returns an iterartor over legal encodings for the instruction.
|
||||
fn legal_encodings<'a>(
|
||||
&'a self,
|
||||
dfg: &'a ir::DataFlowGraph,
|
||||
func: &'a ir::Function,
|
||||
inst: &'a ir::InstructionData,
|
||||
ctrl_typevar: ir::Type,
|
||||
) -> 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.
|
||||
fn encode(
|
||||
&self,
|
||||
dfg: &ir::DataFlowGraph,
|
||||
func: &ir::Function,
|
||||
inst: &ir::InstructionData,
|
||||
ctrl_typevar: ir::Type,
|
||||
) -> 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())
|
||||
}
|
||||
|
||||
|
||||
@@ -66,14 +66,14 @@ impl TargetIsa for Isa {
|
||||
|
||||
fn legal_encodings<'a>(
|
||||
&'a self,
|
||||
dfg: &'a ir::DataFlowGraph,
|
||||
func: &'a ir::Function,
|
||||
inst: &'a ir::InstructionData,
|
||||
ctrl_typevar: ir::Type,
|
||||
) -> Encodings<'a> {
|
||||
lookup_enclist(
|
||||
ctrl_typevar,
|
||||
inst,
|
||||
dfg,
|
||||
func,
|
||||
self.cpumode,
|
||||
&enc_tables::LEVEL2[..],
|
||||
&enc_tables::ENCLISTS[..],
|
||||
@@ -113,7 +113,7 @@ impl TargetIsa for Isa {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ir::{DataFlowGraph, InstructionData, Opcode};
|
||||
use ir::{Function, InstructionData, Opcode};
|
||||
use ir::{immediates, types};
|
||||
use isa;
|
||||
use settings::{self, Configurable};
|
||||
@@ -133,10 +133,10 @@ mod tests {
|
||||
let shared_flags = settings::Flags::new(&shared_builder);
|
||||
let isa = isa::lookup("riscv").unwrap().finish(shared_flags);
|
||||
|
||||
let mut dfg = DataFlowGraph::new();
|
||||
let ebb = dfg.make_ebb();
|
||||
let arg64 = dfg.append_ebb_param(ebb, types::I64);
|
||||
let arg32 = dfg.append_ebb_param(ebb, types::I32);
|
||||
let mut func = Function::new();
|
||||
let ebb = func.dfg.make_ebb();
|
||||
let arg64 = func.dfg.append_ebb_param(ebb, types::I64);
|
||||
let arg32 = func.dfg.append_ebb_param(ebb, types::I32);
|
||||
|
||||
// Try to encode iadd_imm.i64 v1, -10.
|
||||
let inst64 = InstructionData::BinaryImm {
|
||||
@@ -147,7 +147,7 @@ mod tests {
|
||||
|
||||
// ADDI is I/0b00100
|
||||
assert_eq!(
|
||||
encstr(&*isa, isa.encode(&dfg, &inst64, types::I64)),
|
||||
encstr(&*isa, isa.encode(&func, &inst64, types::I64)),
|
||||
"Ii#04"
|
||||
);
|
||||
|
||||
@@ -159,7 +159,7 @@ mod tests {
|
||||
};
|
||||
|
||||
// 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.
|
||||
let inst32 = InstructionData::BinaryImm {
|
||||
@@ -170,7 +170,7 @@ mod tests {
|
||||
|
||||
// ADDIW is I/0b00110
|
||||
assert_eq!(
|
||||
encstr(&*isa, isa.encode(&dfg, &inst32, types::I32)),
|
||||
encstr(&*isa, isa.encode(&func, &inst32, types::I32)),
|
||||
"Ii#06"
|
||||
);
|
||||
}
|
||||
@@ -183,10 +183,10 @@ mod tests {
|
||||
let shared_flags = settings::Flags::new(&shared_builder);
|
||||
let isa = isa::lookup("riscv").unwrap().finish(shared_flags);
|
||||
|
||||
let mut dfg = DataFlowGraph::new();
|
||||
let ebb = dfg.make_ebb();
|
||||
let arg64 = dfg.append_ebb_param(ebb, types::I64);
|
||||
let arg32 = dfg.append_ebb_param(ebb, types::I32);
|
||||
let mut func = Function::new();
|
||||
let ebb = func.dfg.make_ebb();
|
||||
let arg64 = func.dfg.append_ebb_param(ebb, types::I64);
|
||||
let arg32 = func.dfg.append_ebb_param(ebb, types::I32);
|
||||
|
||||
// Try to encode iadd_imm.i64 v1, -10.
|
||||
let inst64 = InstructionData::BinaryImm {
|
||||
@@ -196,7 +196,7 @@ mod tests {
|
||||
};
|
||||
|
||||
// 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.
|
||||
let inst64_large = InstructionData::BinaryImm {
|
||||
@@ -206,7 +206,7 @@ mod tests {
|
||||
};
|
||||
|
||||
// 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.
|
||||
let inst32 = InstructionData::BinaryImm {
|
||||
@@ -217,7 +217,7 @@ mod tests {
|
||||
|
||||
// ADDI is I/0b00100
|
||||
assert_eq!(
|
||||
encstr(&*isa, isa.encode(&dfg, &inst32, types::I32)),
|
||||
encstr(&*isa, isa.encode(&func, &inst32, types::I32)),
|
||||
"Ii#04"
|
||||
);
|
||||
|
||||
@@ -227,7 +227,7 @@ mod tests {
|
||||
args: [arg32, arg32],
|
||||
};
|
||||
|
||||
assert!(isa.encode(&dfg, &mul32, types::I32).is_err());
|
||||
assert!(isa.encode(&func, &mul32, types::I32).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -243,16 +243,19 @@ mod tests {
|
||||
|
||||
let isa = isa_builder.finish(shared_flags);
|
||||
|
||||
let mut dfg = DataFlowGraph::new();
|
||||
let ebb = dfg.make_ebb();
|
||||
let arg32 = dfg.append_ebb_param(ebb, types::I32);
|
||||
let mut func = Function::new();
|
||||
let ebb = func.dfg.make_ebb();
|
||||
let arg32 = func.dfg.append_ebb_param(ebb, types::I32);
|
||||
|
||||
// Create an imul.i32 which is encodable in RV32M.
|
||||
let mul32 = InstructionData::Binary {
|
||||
opcode: Opcode::Imul,
|
||||
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>(
|
||||
&'a self,
|
||||
dfg: &'a ir::DataFlowGraph,
|
||||
func: &'a ir::Function,
|
||||
inst: &'a ir::InstructionData,
|
||||
ctrl_typevar: ir::Type,
|
||||
) -> Encodings<'a> {
|
||||
lookup_enclist(
|
||||
ctrl_typevar,
|
||||
inst,
|
||||
dfg,
|
||||
func,
|
||||
self.cpumode,
|
||||
&enc_tables::LEVEL2[..],
|
||||
&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);
|
||||
|
||||
// TODO: Can libcalls be colocated in some circumstances?
|
||||
func.import_function(ir::ExtFuncData {
|
||||
name: ir::ExternalName::LibCall(libcall),
|
||||
signature: sigref,
|
||||
colocated: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
//! Some of these predicates may be unused in certain ISA configurations, so we suppress the
|
||||
//! dead code warning.
|
||||
|
||||
use ir;
|
||||
|
||||
/// Check that `x` is the same as `y`.
|
||||
#[allow(dead_code)]
|
||||
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)
|
||||
}
|
||||
|
||||
#[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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1025,7 +1025,7 @@ impl<'a> Verifier<'a> {
|
||||
let encoding = self.func.encodings[inst];
|
||||
if encoding.is_legal() {
|
||||
let mut encodings = isa.legal_encodings(
|
||||
&self.func.dfg,
|
||||
&self.func,
|
||||
&self.func.dfg[inst],
|
||||
self.func.dfg.ctrl_typevar(inst),
|
||||
).peekable();
|
||||
@@ -1045,7 +1045,7 @@ impl<'a> Verifier<'a> {
|
||||
let mut multiple_encodings = false;
|
||||
|
||||
for enc in isa.legal_encodings(
|
||||
&self.func.dfg,
|
||||
&self.func,
|
||||
&self.func.dfg[inst],
|
||||
self.func.dfg.ctrl_typevar(inst),
|
||||
)
|
||||
@@ -1099,7 +1099,7 @@ impl<'a> Verifier<'a> {
|
||||
if let Some(text) = needs_enc {
|
||||
// This instruction needs an encoding, so generate an error.
|
||||
// Provide the ISA default encoding as a hint.
|
||||
match self.func.dfg.encode(inst, isa) {
|
||||
match self.func.encode(inst, isa) {
|
||||
Ok(enc) => {
|
||||
return err!(
|
||||
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 = regs.as_ref();
|
||||
|
||||
write!(w, "function ")?;
|
||||
write_spec(w, func, regs)?;
|
||||
writeln!(w, " {{")?;
|
||||
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.
|
||||
|
||||
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(
|
||||
|
||||
@@ -133,7 +133,7 @@ impl SubTest for TestBinEmit {
|
||||
// constraints.
|
||||
if let Some(enc) = {
|
||||
let mut legal_encodings = isa.legal_encodings(
|
||||
&func.dfg,
|
||||
&func,
|
||||
&func.dfg[inst],
|
||||
func.dfg.ctrl_typevar(inst),
|
||||
).filter(|e| {
|
||||
@@ -251,7 +251,7 @@ impl SubTest for TestBinEmit {
|
||||
|
||||
// Do any encodings exist?
|
||||
let encodings = isa.legal_encodings(
|
||||
&func.dfg,
|
||||
&func,
|
||||
&func.dfg[inst],
|
||||
func.dfg.ctrl_typevar(inst),
|
||||
).map(|e| encinfo.display(e))
|
||||
|
||||
@@ -144,6 +144,7 @@ impl<'a> Context<'a> {
|
||||
while self.function.global_vars.next_key().index() <= gv.index() {
|
||||
self.function.create_global_var(GlobalVarData::Sym {
|
||||
name: ExternalName::testcase(""),
|
||||
colocated: false,
|
||||
});
|
||||
}
|
||||
self.function.global_vars[gv] = data;
|
||||
@@ -208,6 +209,7 @@ impl<'a> Context<'a> {
|
||||
self.function.import_function(ExtFuncData {
|
||||
name: ExternalName::testcase(""),
|
||||
signature: SigRef::reserved_value(),
|
||||
colocated: false,
|
||||
});
|
||||
}
|
||||
self.function.dfg.ext_funcs[fn_] = data;
|
||||
@@ -760,7 +762,7 @@ impl<'a> Parser<'a> {
|
||||
|
||||
// Parse a whole function definition.
|
||||
//
|
||||
// function ::= * function-spec "{" preamble function-body "}"
|
||||
// function ::= * "function" name signature "{" preamble function-body "}"
|
||||
//
|
||||
fn parse_function(
|
||||
&mut self,
|
||||
@@ -772,10 +774,19 @@ impl<'a> Parser<'a> {
|
||||
debug_assert!(self.comments.is_empty());
|
||||
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);
|
||||
|
||||
// function ::= function-spec * "{" preamble function-body "}"
|
||||
// function ::= "function" name signature * "{" preamble function-body "}"
|
||||
self.match_token(
|
||||
Token::LBrace,
|
||||
"expected '{' before function body",
|
||||
@@ -784,11 +795,11 @@ impl<'a> Parser<'a> {
|
||||
self.token();
|
||||
self.claim_gathered_comments(AnyEntity::Function);
|
||||
|
||||
// function ::= function-spec "{" * preamble function-body "}"
|
||||
// function ::= "function" name signature "{" * preamble function-body "}"
|
||||
self.parse_preamble(&mut ctx)?;
|
||||
// function ::= function-spec "{" preamble * function-body "}"
|
||||
// function ::= "function" name signature "{" preamble * function-body "}"
|
||||
self.parse_function_body(&mut ctx)?;
|
||||
// function ::= function-spec "{" preamble function-body * "}"
|
||||
// function ::= "function" name signature "{" preamble function-body * "}"
|
||||
self.match_token(
|
||||
Token::RBrace,
|
||||
"expected '}' after function body",
|
||||
@@ -808,29 +819,9 @@ impl<'a> Parser<'a> {
|
||||
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.
|
||||
//
|
||||
// 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 { ... }
|
||||
//
|
||||
@@ -1095,7 +1086,7 @@ impl<'a> Parser<'a> {
|
||||
// global-var-decl ::= * GlobalVar(gv) "=" global-var-desc
|
||||
// global-var-desc ::= "vmctx" offset32
|
||||
// | "deref" "(" GlobalVar(base) ")" offset32
|
||||
// | "globalsym" name
|
||||
// | globalsym ["colocated"] name
|
||||
//
|
||||
fn parse_global_var_decl(&mut self) -> Result<(GlobalVar, GlobalVarData)> {
|
||||
let gv = self.match_gv("expected global variable number: gv«n»")?;
|
||||
@@ -1124,8 +1115,9 @@ impl<'a> Parser<'a> {
|
||||
GlobalVarData::Deref { base, offset }
|
||||
}
|
||||
"globalsym" => {
|
||||
let colocated = self.optional(Token::Identifier("colocated"));
|
||||
let name = self.parse_external_name()?;
|
||||
GlobalVarData::Sym { name }
|
||||
GlobalVarData::Sym { name, colocated }
|
||||
}
|
||||
other => return err!(self.loc, "Unknown global variable kind '{}'", other),
|
||||
};
|
||||
@@ -1237,8 +1229,8 @@ impl<'a> Parser<'a> {
|
||||
//
|
||||
// Two variants:
|
||||
//
|
||||
// function-decl ::= FuncRef(fnref) "=" function-spec
|
||||
// FuncRef(fnref) "=" SigRef(sig) name
|
||||
// function-decl ::= FuncRef(fnref) "=" ["colocated"]" name function-decl-sig
|
||||
// function-decl-sig ::= SigRef(sig) | signature
|
||||
//
|
||||
// The first variant allocates a new signature reference. The second references an existing
|
||||
// signature which must be declared first.
|
||||
@@ -1250,9 +1242,19 @@ impl<'a> Parser<'a> {
|
||||
"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() {
|
||||
Some(Token::Identifier("function")) => {
|
||||
let (loc, name, sig) = self.parse_function_spec(ctx.unique_isa)?;
|
||||
Some(Token::LPar) => {
|
||||
// function-decl ::= FuncRef(fnref) "=" ["colocated"] name * signature
|
||||
let sig = self.parse_signature(ctx.unique_isa)?;
|
||||
let sigref = ctx.function.import_signature(sig);
|
||||
ctx.map.def_entity(sigref.into(), &loc).expect(
|
||||
"duplicate SigRef entities created",
|
||||
@@ -1260,6 +1262,7 @@ impl<'a> Parser<'a> {
|
||||
ExtFuncData {
|
||||
name,
|
||||
signature: sigref,
|
||||
colocated,
|
||||
}
|
||||
}
|
||||
Some(Token::SigRef(sig_src)) => {
|
||||
@@ -1271,10 +1274,10 @@ impl<'a> Parser<'a> {
|
||||
};
|
||||
ctx.check_sig(sig, &self.loc)?;
|
||||
self.consume();
|
||||
let name = self.parse_external_name()?;
|
||||
ExtFuncData {
|
||||
name,
|
||||
signature: sig,
|
||||
colocated,
|
||||
}
|
||||
}
|
||||
_ => 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.
|
||||
let signature = func.import_signature(self.vmctx_sig(sigidx));
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user