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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@@ -12,7 +12,7 @@ isa x86 haswell
; Tests from binary32.cton affected by allones_funcaddrs.
function %I32() {
sig0 = ()
fn0 = function %foo()
fn0 = %foo()
ebb0:

View File

@@ -13,7 +13,7 @@ isa x86 haswell
; Tests from binary64.cton affected by allones_funcaddrs.
function %I64() {
sig0 = ()
fn0 = function %foo()
fn0 = %foo()
ebb0:

View File

@@ -10,7 +10,7 @@ isa x86 haswell
function %I32() {
sig0 = ()
fn0 = function %foo()
fn0 = %foo()
gv0 = globalsym %some_gv

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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