diff --git a/cranelift/docs/callex.cton b/cranelift/docs/callex.cton index 837f9ea6e7..26abda55b4 100644 --- a/cranelift/docs/callex.cton +++ b/cranelift/docs/callex.cton @@ -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 diff --git a/cranelift/docs/langref.rst b/cranelift/docs/langref.rst index 14f44bab53..070b27ee37 100644 --- a/cranelift/docs/langref.rst +++ b/cranelift/docs/langref.rst @@ -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. diff --git a/cranelift/filetests/isa/riscv/binary32.cton b/cranelift/filetests/isa/riscv/binary32.cton index 459d9d7419..7837672bcc 100644 --- a/cranelift/filetests/isa/riscv/binary32.cton +++ b/cranelift/filetests/isa/riscv/binary32.cton @@ -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 diff --git a/cranelift/filetests/isa/riscv/legalize-abi.cton b/cranelift/filetests/isa/riscv/legalize-abi.cton index 44865120ea..df60b91f81 100644 --- a/cranelift/filetests/isa/riscv/legalize-abi.cton +++ b/cranelift/filetests/isa/riscv/legalize-abi.cton @@ -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) diff --git a/cranelift/filetests/isa/riscv/parse-encoding.cton b/cranelift/filetests/isa/riscv/parse-encoding.cton index 0fc0879f38..b8220b1688 100644 --- a/cranelift/filetests/isa/riscv/parse-encoding.cton +++ b/cranelift/filetests/isa/riscv/parse-encoding.cton @@ -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 diff --git a/cranelift/filetests/isa/riscv/verify-encoding.cton b/cranelift/filetests/isa/riscv/verify-encoding.cton index 725b9e5744..1aaedf1c89 100644 --- a/cranelift/filetests/isa/riscv/verify-encoding.cton +++ b/cranelift/filetests/isa/riscv/verify-encoding.cton @@ -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 diff --git a/cranelift/filetests/isa/x86/abi64.cton b/cranelift/filetests/isa/x86/abi64.cton index 2cb63c0dab..40321f90fb 100644 --- a/cranelift/filetests/isa/x86/abi64.cton +++ b/cranelift/filetests/isa/x86/abi64.cton @@ -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) diff --git a/cranelift/filetests/isa/x86/allones_funcaddrs32.cton b/cranelift/filetests/isa/x86/allones_funcaddrs32.cton index d549c8d5e3..c4c078470b 100644 --- a/cranelift/filetests/isa/x86/allones_funcaddrs32.cton +++ b/cranelift/filetests/isa/x86/allones_funcaddrs32.cton @@ -12,7 +12,7 @@ isa x86 haswell ; Tests from binary32.cton affected by allones_funcaddrs. function %I32() { sig0 = () - fn0 = function %foo() + fn0 = %foo() ebb0: diff --git a/cranelift/filetests/isa/x86/allones_funcaddrs64.cton b/cranelift/filetests/isa/x86/allones_funcaddrs64.cton index 478c875e3d..ca3cf1ab69 100644 --- a/cranelift/filetests/isa/x86/allones_funcaddrs64.cton +++ b/cranelift/filetests/isa/x86/allones_funcaddrs64.cton @@ -13,7 +13,7 @@ isa x86 haswell ; Tests from binary64.cton affected by allones_funcaddrs. function %I64() { sig0 = () - fn0 = function %foo() + fn0 = %foo() ebb0: diff --git a/cranelift/filetests/isa/x86/binary32.cton b/cranelift/filetests/isa/x86/binary32.cton index 342af80140..842f941779 100644 --- a/cranelift/filetests/isa/x86/binary32.cton +++ b/cranelift/filetests/isa/x86/binary32.cton @@ -10,7 +10,7 @@ isa x86 haswell function %I32() { sig0 = () - fn0 = function %foo() + fn0 = %foo() gv0 = globalsym %some_gv diff --git a/cranelift/filetests/isa/x86/binary64-pic.cton b/cranelift/filetests/isa/x86/binary64-pic.cton index e1cd79ac71..f3b838795a 100644 --- a/cranelift/filetests/isa/x86/binary64-pic.cton +++ b/cranelift/filetests/isa/x86/binary64-pic.cton @@ -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 } diff --git a/cranelift/filetests/isa/x86/binary64.cton b/cranelift/filetests/isa/x86/binary64.cton index 37f026f2ab..6ecc086231 100644 --- a/cranelift/filetests/isa/x86/binary64.cton +++ b/cranelift/filetests/isa/x86/binary64.cton @@ -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 diff --git a/cranelift/filetests/isa/x86/legalize-libcall.cton b/cranelift/filetests/isa/x86/legalize-libcall.cton index bbe332170e..70d392e6e4 100644 --- a/cranelift/filetests/isa/x86/legalize-libcall.cton +++ b/cranelift/filetests/isa/x86/legalize-libcall.cton @@ -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) diff --git a/cranelift/filetests/isa/x86/prologue-epilogue.cton b/cranelift/filetests/isa/x86/prologue-epilogue.cton index eee6917b1b..9ed37274ca 100644 --- a/cranelift/filetests/isa/x86/prologue-epilogue.cton +++ b/cranelift/filetests/isa/x86/prologue-epilogue.cton @@ -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 diff --git a/cranelift/filetests/parser/call.cton b/cranelift/filetests/parser/call.cton index 3413696caf..9d1e8def02 100644 --- a/cranelift/filetests/parser/call.cton +++ b/cranelift/filetests/parser/call.cton @@ -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 diff --git a/cranelift/filetests/regalloc/basic.cton b/cranelift/filetests/regalloc/basic.cton index b9399f464d..c24f87c1f9 100644 --- a/cranelift/filetests/regalloc/basic.cton +++ b/cranelift/filetests/regalloc/basic.cton @@ -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) diff --git a/cranelift/filetests/regalloc/coalescing-207.cton b/cranelift/filetests/regalloc/coalescing-207.cton index 9066bd7c4f..0deaa454f4 100644 --- a/cranelift/filetests/regalloc/coalescing-207.cton +++ b/cranelift/filetests/regalloc/coalescing-207.cton @@ -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 diff --git a/cranelift/filetests/regalloc/reload-208.cton b/cranelift/filetests/regalloc/reload-208.cton index 8ccd3e6ef3..bb775acebb 100644 --- a/cranelift/filetests/regalloc/reload-208.cton +++ b/cranelift/filetests/regalloc/reload-208.cton @@ -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 diff --git a/cranelift/filetests/regalloc/reload.cton b/cranelift/filetests/regalloc/reload.cton index 5e62db3213..d0a9646808 100644 --- a/cranelift/filetests/regalloc/reload.cton +++ b/cranelift/filetests/regalloc/reload.cton @@ -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() diff --git a/cranelift/filetests/regalloc/spill.cton b/cranelift/filetests/regalloc/spill.cton index fb822e1a6f..7205d6a851 100644 --- a/cranelift/filetests/regalloc/spill.cton +++ b/cranelift/filetests/regalloc/spill.cton @@ -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) diff --git a/cranelift/filetests/verifier/type_check.cton b/cranelift/filetests/verifier/type_check.cton index 4bd386efbf..8783c4aced 100644 --- a/cranelift/filetests/verifier/type_check.cton +++ b/cranelift/filetests/verifier/type_check.cton @@ -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 diff --git a/lib/cretonne/meta/base/predicates.py b/lib/cretonne/meta/base/predicates.py new file mode 100644 index 0000000000..1a6b4c2c75 --- /dev/null +++ b/lib/cretonne/meta/base/predicates.py @@ -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',)) diff --git a/lib/cretonne/meta/cdsl/predicates.py b/lib/cretonne/meta/cdsl/predicates.py index 9a28fd9945..13957c5d02 100644 --- a/lib/cretonne/meta/cdsl/predicates.py +++ b/lib/cretonne/meta/cdsl/predicates.py @@ -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()) diff --git a/lib/cretonne/meta/gen_encoding.py b/lib/cretonne/meta/gen_encoding.py index 61ceb8d498..e3915100a2 100644 --- a/lib/cretonne/meta/gen_encoding.py +++ b/lib/cretonne/meta/gen_encoding.py @@ -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( diff --git a/lib/cretonne/meta/gen_legalizer.py b/lib/cretonne/meta/gen_legalizer.py index 9051337238..104574b7b6 100644 --- a/lib/cretonne/meta/gen_legalizer.py +++ b/lib/cretonne/meta/gen_legalizer.py @@ -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') diff --git a/lib/cretonne/meta/isa/x86/encodings.py b/lib/cretonne/meta/isa/x86/encodings.py index 915e75436d..c120fe1a58 100644 --- a/lib/cretonne/meta/isa/x86/encodings.py +++ b/lib/cretonne/meta/isa/x86/encodings.py @@ -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)) diff --git a/lib/cretonne/meta/isa/x86/recipes.py b/lib/cretonne/meta/isa/x86/recipes.py index 42ba7c3852..5d0b045ead 100644 --- a/lib/cretonne/meta/isa/x86/recipes.py +++ b/lib/cretonne/meta/isa/x86/recipes.py @@ -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, diff --git a/lib/cretonne/src/binemit/relaxation.rs b/lib/cretonne/src/binemit/relaxation.rs index 44a6611c3c..93c6afedec 100644 --- a/lib/cretonne/src/binemit/relaxation.rs +++ b/lib/cretonne/src/binemit/relaxation.rs @@ -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) { diff --git a/lib/cretonne/src/cursor.rs b/lib/cretonne/src/cursor.rs index 082889a693..865530341d 100644 --- a/lib/cretonne/src/cursor.rs +++ b/lib/cretonne/src/cursor.rs @@ -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, ) { diff --git a/lib/cretonne/src/ir/dfg.rs b/lib/cretonne/src/ir/dfg.rs index 24f18ff332..c2f07beb25 100644 --- a/lib/cretonne/src/ir/dfg.rs +++ b/lib/cretonne/src/ir/dfg.rs @@ -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 { - isa.encode(&self, &self[inst], self.ctrl_typevar(inst)) - } } /// Allow immutable access to instructions via indexing. diff --git a/lib/cretonne/src/ir/extfunc.rs b/lib/cretonne/src/ir/extfunc.rs index 45c44c952a..474b463727 100644 --- a/lib/cretonne/src/ir/extfunc.rs +++ b/lib/cretonne/src/ir/extfunc.rs @@ -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) } } diff --git a/lib/cretonne/src/ir/function.rs b/lib/cretonne/src/ir/function.rs index f7dee1b918..5e70ff3780 100644 --- a/lib/cretonne/src/ir/function.rs +++ b/lib/cretonne/src/ir/function.rs @@ -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 { + isa.encode(&self, &self.dfg[inst], self.dfg.ctrl_typevar(inst)) } } diff --git a/lib/cretonne/src/ir/globalvar.rs b/lib/cretonne/src/ir/globalvar.rs index 228f691d84..ad75496362 100644 --- a/lib/cretonne/src/ir/globalvar.rs +++ b/lib/cretonne/src/ir/globalvar.rs @@ -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) + } } } } diff --git a/lib/cretonne/src/isa/arm32/mod.rs b/lib/cretonne/src/isa/arm32/mod.rs index dbef9aaf8a..967c21a1ba 100644 --- a/lib/cretonne/src/isa/arm32/mod.rs +++ b/lib/cretonne/src/isa/arm32/mod.rs @@ -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[..], diff --git a/lib/cretonne/src/isa/arm64/mod.rs b/lib/cretonne/src/isa/arm64/mod.rs index eeddec74fd..27c95f51b3 100644 --- a/lib/cretonne/src/isa/arm64/mod.rs +++ b/lib/cretonne/src/isa/arm64/mod.rs @@ -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[..], diff --git a/lib/cretonne/src/isa/enc_tables.rs b/lib/cretonne/src/isa/enc_tables.rs index c987606fa6..b4c9bd86ba 100644 --- a/lib/cretonne/src/isa/enc_tables.rs +++ b/lib/cretonne/src/isa/enc_tables.rs @@ -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 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 + Copy> Table for [Level2Entry] { pub fn lookup_enclist<'a, OffT1, OffT2>( ctrl_typevar: Type, inst: &'a InstructionData, - dfg: &'a DataFlowGraph, + func: &'a Function, level1_table: &'static [Level1Entry], level2_table: &'static [Level2Entry], 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) diff --git a/lib/cretonne/src/isa/mod.rs b/lib/cretonne/src/isa/mod.rs index 5ee3359d50..d851d8d9a0 100644 --- a/lib/cretonne/src/isa/mod.rs +++ b/lib/cretonne/src/isa/mod.rs @@ -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 { - 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()) } diff --git a/lib/cretonne/src/isa/riscv/mod.rs b/lib/cretonne/src/isa/riscv/mod.rs index c81d3baceb..af993aad0d 100644 --- a/lib/cretonne/src/isa/riscv/mod.rs +++ b/lib/cretonne/src/isa/riscv/mod.rs @@ -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" + ); } } diff --git a/lib/cretonne/src/isa/x86/mod.rs b/lib/cretonne/src/isa/x86/mod.rs index a272420421..6bbde318a5 100644 --- a/lib/cretonne/src/isa/x86/mod.rs +++ b/lib/cretonne/src/isa/x86/mod.rs @@ -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[..], diff --git a/lib/cretonne/src/legalizer/libcall.rs b/lib/cretonne/src/legalizer/libcall.rs index 2bb0f1292c..dafc353903 100644 --- a/lib/cretonne/src/legalizer/libcall.rs +++ b/lib/cretonne/src/legalizer/libcall.rs @@ -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, }) } diff --git a/lib/cretonne/src/predicates.rs b/lib/cretonne/src/predicates.rs index 618369291d..333ca8eb31 100644 --- a/lib/cretonne/src/predicates.rs +++ b/lib/cretonne/src/predicates.rs @@ -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 + Copy>(x: T, y: O) -> bool { @@ -31,6 +33,19 @@ pub fn is_unsigned_int>(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::*; diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index 4000e5883f..abda326c03 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -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, diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index 2ed8da87e9..22d78bdb82 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -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( diff --git a/lib/filetests/src/test_binemit.rs b/lib/filetests/src/test_binemit.rs index 775e014600..ce2e97e888 100644 --- a/lib/filetests/src/test_binemit.rs +++ b/lib/filetests/src/test_binemit.rs @@ -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)) diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index f23f644a0e..66a66ac18c 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -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"), diff --git a/lib/wasm/src/environ/dummy.rs b/lib/wasm/src/environ/dummy.rs index a71dddda3d..2bac8ed783 100644 --- a/lib/wasm/src/environ/dummy.rs +++ b/lib/wasm/src/environ/dummy.rs @@ -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(