Add a func_addr instruction.
Get the callable address of a function. Use for long distance calls and for creating arguments to call_indirect in general.
This commit is contained in:
@@ -411,13 +411,7 @@ This simple example illustrates direct function calls and signatures::
|
|||||||
Indirect function calls use a signature declared in the preamble.
|
Indirect function calls use a signature declared in the preamble.
|
||||||
|
|
||||||
.. autoinst:: call_indirect
|
.. autoinst:: call_indirect
|
||||||
|
.. autoinst:: func_addr
|
||||||
.. todo:: Define safe indirect function calls.
|
|
||||||
|
|
||||||
The :inst:`call_indirect` instruction is dangerous to use in a sandboxed
|
|
||||||
environment since it is not easy to verify the callee address.
|
|
||||||
We need a table-driven indirect call instruction, similar to
|
|
||||||
:inst:`br_table`.
|
|
||||||
|
|
||||||
|
|
||||||
Memory
|
Memory
|
||||||
|
|||||||
@@ -69,6 +69,19 @@ ebb0(v0: i64):
|
|||||||
; check: $v3, $v4 = call_indirect $sig2, $v1()
|
; check: $v3, $v4 = call_indirect $sig2, $v1()
|
||||||
; check: return
|
; check: return
|
||||||
|
|
||||||
|
function %long_call() {
|
||||||
|
sig0 = ()
|
||||||
|
fn0 = sig0 %none
|
||||||
|
|
||||||
|
ebb0:
|
||||||
|
v0 = func_addr.i32 fn0
|
||||||
|
call_indirect sig0, v0()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
; check: $v0 = func_addr.i32 $fn0
|
||||||
|
; check: call_indirect $sig0, $v0()
|
||||||
|
; check: return
|
||||||
|
|
||||||
; Special purpose function arguments
|
; Special purpose function arguments
|
||||||
function %special1(i32 sret, i32 fp, i32 csr, i32 link) -> i32 link, i32 fp, i32 csr, i32 sret {
|
function %special1(i32 sret, i32 fp, i32 csr, i32 link) -> i32 link, i32 fp, i32 csr, i32 sret {
|
||||||
ebb0(v1: i32, v2: i32, v3: i32, v4: i32):
|
ebb0(v1: i32, v2: i32, v3: i32, v4: i32):
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ BranchTable = InstructionFormat(VALUE, entities.jump_table)
|
|||||||
|
|
||||||
Call = InstructionFormat(func_ref, VARIABLE_ARGS)
|
Call = InstructionFormat(func_ref, VARIABLE_ARGS)
|
||||||
IndirectCall = InstructionFormat(sig_ref, VALUE, VARIABLE_ARGS)
|
IndirectCall = InstructionFormat(sig_ref, VALUE, VARIABLE_ARGS)
|
||||||
|
FuncAddr = InstructionFormat(func_ref)
|
||||||
|
|
||||||
Load = InstructionFormat(memflags, VALUE, offset32)
|
Load = InstructionFormat(memflags, VALUE, offset32)
|
||||||
Store = InstructionFormat(memflags, VALUE, VALUE, offset32)
|
Store = InstructionFormat(memflags, VALUE, VALUE, offset32)
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ MemTo = TypeVar(
|
|||||||
'MemTo', 'Any type that can be stored in memory',
|
'MemTo', 'Any type that can be stored in memory',
|
||||||
ints=True, floats=True, simd=True)
|
ints=True, floats=True, simd=True)
|
||||||
|
|
||||||
|
addr = Operand('addr', iAddr)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Control flow
|
# Control flow
|
||||||
#
|
#
|
||||||
@@ -185,6 +187,18 @@ call_indirect = Instruction(
|
|||||||
""",
|
""",
|
||||||
ins=(SIG, callee, args), outs=rvals, is_call=True)
|
ins=(SIG, callee, args), outs=rvals, is_call=True)
|
||||||
|
|
||||||
|
func_addr = Instruction(
|
||||||
|
'func_addr', r"""
|
||||||
|
Get the address of a function.
|
||||||
|
|
||||||
|
Compute the absolute address of a function declared in the preamble.
|
||||||
|
The returned address can be used as a ``callee`` argument to
|
||||||
|
:inst:`call_indirect`. This is also a method for calling functions that
|
||||||
|
are too far away to be addressable by a direct :inst:`call`
|
||||||
|
instruction.
|
||||||
|
""",
|
||||||
|
ins=FN, outs=addr)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Memory operations
|
# Memory operations
|
||||||
#
|
#
|
||||||
@@ -194,7 +208,6 @@ Offset = Operand('Offset', offset32, 'Byte offset from base address')
|
|||||||
x = Operand('x', Mem, doc='Value to be stored')
|
x = Operand('x', Mem, doc='Value to be stored')
|
||||||
a = Operand('a', Mem, doc='Value loaded')
|
a = Operand('a', Mem, doc='Value loaded')
|
||||||
p = Operand('p', iAddr)
|
p = Operand('p', iAddr)
|
||||||
addr = Operand('addr', iAddr)
|
|
||||||
Flags = Operand('Flags', memflags)
|
Flags = Operand('Flags', memflags)
|
||||||
|
|
||||||
load = Instruction(
|
load = Instruction(
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ pub enum InstructionData {
|
|||||||
sig_ref: SigRef,
|
sig_ref: SigRef,
|
||||||
args: ValueList,
|
args: ValueList,
|
||||||
},
|
},
|
||||||
|
FuncAddr { opcode: Opcode, func_ref: FuncRef },
|
||||||
StackLoad {
|
StackLoad {
|
||||||
opcode: Opcode,
|
opcode: Opcode,
|
||||||
stack_slot: StackSlot,
|
stack_slot: StackSlot,
|
||||||
|
|||||||
@@ -306,6 +306,9 @@ impl<'a> Verifier<'a> {
|
|||||||
self.verify_sig_ref(inst, sig_ref)?;
|
self.verify_sig_ref(inst, sig_ref)?;
|
||||||
self.verify_value_list(inst, args)?;
|
self.verify_value_list(inst, args)?;
|
||||||
}
|
}
|
||||||
|
FuncAddr { func_ref, .. } => {
|
||||||
|
self.verify_func_ref(inst, func_ref)?;
|
||||||
|
}
|
||||||
StackLoad { stack_slot, .. } |
|
StackLoad { stack_slot, .. } |
|
||||||
StackStore { stack_slot, .. } => {
|
StackStore { stack_slot, .. } => {
|
||||||
self.verify_stack_slot(inst, stack_slot)?;
|
self.verify_stack_slot(inst, stack_slot)?;
|
||||||
|
|||||||
@@ -346,6 +346,7 @@ pub fn write_operands(
|
|||||||
DisplayValues(&args[1..])
|
DisplayValues(&args[1..])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
|
||||||
StackLoad { stack_slot, offset, .. } => write!(w, " {}{}", stack_slot, offset),
|
StackLoad { stack_slot, offset, .. } => write!(w, " {}{}", stack_slot, offset),
|
||||||
StackStore {
|
StackStore {
|
||||||
arg,
|
arg,
|
||||||
|
|||||||
@@ -2098,6 +2098,14 @@ impl<'a> Parser<'a> {
|
|||||||
args: args.into_value_list(&[callee], &mut ctx.function.dfg.value_lists),
|
args: args.into_value_list(&[callee], &mut ctx.function.dfg.value_lists),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
InstructionFormat::FuncAddr => {
|
||||||
|
let func_ref = self.match_fn("expected function reference").and_then(
|
||||||
|
|num| {
|
||||||
|
ctx.get_fn(num, &self.loc)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
InstructionData::FuncAddr { opcode, func_ref }
|
||||||
|
}
|
||||||
InstructionFormat::BranchTable => {
|
InstructionFormat::BranchTable => {
|
||||||
let arg = self.match_value("expected SSA value operand")?;
|
let arg = self.match_value("expected SSA value operand")?;
|
||||||
self.match_token(
|
self.match_token(
|
||||||
|
|||||||
Reference in New Issue
Block a user