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:
Dan Gohman
2018-04-13 15:00:09 -07:00
committed by GitHub
parent 645fa3e858
commit 0e57f3d0ea
46 changed files with 312 additions and 164 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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
} }

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View 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',))

View File

@@ -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())

View File

@@ -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(

View File

@@ -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')

View File

@@ -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))

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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,
) { ) {

View File

@@ -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.

View File

@@ -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)
} }
} }

View File

@@ -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))
} }
} }

View File

@@ -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)
}
} }
} }
} }

View File

@@ -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[..],

View File

@@ -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[..],

View File

@@ -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)

View File

@@ -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())
} }

View File

@@ -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"
);
} }
} }

View File

@@ -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[..],

View File

@@ -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,
}) })
} }

View File

@@ -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::*;

View File

@@ -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,

View File

@@ -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(

View File

@@ -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))

View File

@@ -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"),

View File

@@ -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(