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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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?
#[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,
) {

View File

@@ -7,7 +7,7 @@ use ir::extfunc::ExtFuncData;
use ir::instructions::{BranchInfo, CallInfo, InstructionData};
use ir::types;
use ir::{Ebb, FuncRef, Inst, SigRef, Signature, Type, Value, ValueList, ValueListPool};
use isa::{Encoding, Legalize, TargetIsa};
use isa::TargetIsa;
use packed_option::ReservedValue;
use std::fmt;
use std::iter;
@@ -660,12 +660,6 @@ impl DataFlowGraph {
self.value_type(self.first_result(inst))
}
}
/// Wrapper around `TargetIsa::encode` for encoding an existing instruction
/// in the `DataFlowGraph`.
pub fn encode(&self, inst: Inst, isa: &TargetIsa) -> Result<Encoding, Legalize> {
isa.encode(&self, &self[inst], self.ctrl_typevar(inst))
}
}
/// Allow immutable access to instructions via indexing.

View File

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

View File

@@ -10,7 +10,7 @@ use ir::{CallConv, DataFlowGraph, ExternalName, Layout, Signature};
use ir::{Ebb, ExtFuncData, FuncRef, GlobalVar, GlobalVarData, Heap, HeapData, JumpTable,
JumpTableData, SigRef, StackSlot, StackSlotData};
use ir::{EbbOffsets, InstEncodings, JumpTables, SourceLocs, StackSlots, ValueLocations};
use isa::{EncInfo, Legalize, TargetIsa};
use isa::{EncInfo, Legalize, TargetIsa, Encoding};
use std::fmt;
use write::write_function;
@@ -177,9 +177,15 @@ impl Function {
}
}
/// Wrapper around `DataFlowGraph::encode` which assigns `inst` the resulting encoding.
/// Wrapper around `encode` which assigns `inst` the resulting encoding.
pub fn update_encoding(&mut self, inst: ir::Inst, isa: &TargetIsa) -> Result<(), Legalize> {
self.dfg.encode(inst, isa).map(|e| self.encodings[inst] = e)
self.encode(inst, isa).map(|e| self.encodings[inst] = e)
}
/// Wrapper around `TargetIsa::encode` for encoding an existing instruction
/// in the `Function`.
pub fn encode(&self, inst: ir::Inst, isa: &TargetIsa) -> Result<Encoding, Legalize> {
isa.encode(&self, &self.dfg[inst], self.dfg.ctrl_typevar(inst))
}
}

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
//! `lib/cretonne/meta/gen_encoding.py`.
use constant_hash::{probe, Table};
use ir::{DataFlowGraph, InstructionData, Opcode, Type};
use ir::{Function, InstructionData, Opcode, Type};
use isa::{Encoding, Legalize};
use settings::PredicateView;
use std::ops::Range;
@@ -20,7 +20,7 @@ pub type RecipePredicate = Option<fn(PredicateView, &InstructionData) -> bool>;
///
/// This is a predicate function that needs to be tested in addition to the recipe predicate. It
/// can't depend on ISA settings.
pub type InstPredicate = fn(&DataFlowGraph, &InstructionData) -> bool;
pub type InstPredicate = fn(&Function, &InstructionData) -> bool;
/// Legalization action to perform when no encoding can be found for an instruction.
///
@@ -106,7 +106,7 @@ impl<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
pub fn lookup_enclist<'a, OffT1, OffT2>(
ctrl_typevar: Type,
inst: &'a InstructionData,
dfg: &'a DataFlowGraph,
func: &'a Function,
level1_table: &'static [Level1Entry<OffT1>],
level2_table: &'static [Level2Entry<OffT2>],
enclist: &'static [EncListEntry],
@@ -150,7 +150,7 @@ where
offset,
legalize,
inst,
dfg,
func,
enclist,
legalize_actions,
recipe_preds,
@@ -177,7 +177,7 @@ pub struct Encodings<'a> {
// Legalization code to use of no encoding is found.
legalize: LegalizeCode,
inst: &'a InstructionData,
dfg: &'a DataFlowGraph,
func: &'a Function,
enclist: &'static [EncListEntry],
legalize_actions: &'static [Legalize],
recipe_preds: &'static [RecipePredicate],
@@ -195,7 +195,7 @@ impl<'a> Encodings<'a> {
offset: usize,
legalize: LegalizeCode,
inst: &'a InstructionData,
dfg: &'a DataFlowGraph,
func: &'a Function,
enclist: &'static [EncListEntry],
legalize_actions: &'static [Legalize],
recipe_preds: &'static [RecipePredicate],
@@ -205,7 +205,7 @@ impl<'a> Encodings<'a> {
Encodings {
offset,
inst,
dfg,
func,
legalize,
isa_preds,
recipe_preds,
@@ -236,7 +236,7 @@ impl<'a> Encodings<'a> {
/// Check an instruction or isa predicate.
fn check_pred(&self, pred: usize) -> bool {
if let Some(&p) = self.inst_preds.get(pred) {
p(self.dfg, self.inst)
p(self.func, self.inst)
} else {
let pred = pred - self.inst_preds.len();
self.isa_preds.test(pred)

View File

@@ -167,7 +167,7 @@ pub trait TargetIsa: fmt::Display {
/// Returns an iterartor over legal encodings for the instruction.
fn legal_encodings<'a>(
&'a self,
dfg: &'a ir::DataFlowGraph,
func: &'a ir::Function,
inst: &'a ir::InstructionData,
ctrl_typevar: ir::Type,
) -> Encodings<'a>;
@@ -180,11 +180,11 @@ pub trait TargetIsa: fmt::Display {
/// This is also the main entry point for determining if an instruction is legal.
fn encode(
&self,
dfg: &ir::DataFlowGraph,
func: &ir::Function,
inst: &ir::InstructionData,
ctrl_typevar: ir::Type,
) -> Result<Encoding, Legalize> {
let mut iter = self.legal_encodings(dfg, inst, ctrl_typevar);
let mut iter = self.legal_encodings(func, inst, ctrl_typevar);
iter.next().ok_or_else(|| iter.legalize())
}

View File

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

View File

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

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);
// TODO: Can libcalls be colocated in some circumstances?
func.import_function(ir::ExtFuncData {
name: ir::ExternalName::LibCall(libcall),
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
//! dead code warning.
use ir;
/// Check that `x` is the same as `y`.
#[allow(dead_code)]
pub fn is_equal<T: Eq + Copy, O: Into<T> + Copy>(x: T, y: O) -> bool {
@@ -31,6 +33,19 @@ pub fn is_unsigned_int<T: Into<i64>>(x: T, wd: u8, sc: u8) -> bool {
u == (u & m)
}
#[allow(dead_code)]
pub fn is_colocated_func(func_ref: ir::FuncRef, func: &ir::Function) -> bool {
func.dfg.ext_funcs[func_ref].colocated
}
#[allow(dead_code)]
pub fn is_colocated_data(global_var: ir::GlobalVar, func: &ir::Function) -> bool {
match func.global_vars[global_var] {
ir::GlobalVarData::Sym { colocated, .. } => colocated,
_ => panic!("is_colocated_data only makes sense for data with symbolic addresses"),
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

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

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

View File

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

View File

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

View File

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