Add a return_reg instruction to the base instruction set.
Register-style return is used by all RISC architectures, so it is natural to have a shared instruction representation.
This commit is contained in:
@@ -384,6 +384,7 @@ preamble`:
|
||||
|
||||
.. autoinst:: call
|
||||
.. autoinst:: x_return
|
||||
.. autoinst:: return_reg
|
||||
|
||||
This simple example illustrates direct function calls and signatures::
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ IndirectCall = InstructionFormat(
|
||||
sig_ref, VALUE, VARIABLE_ARGS,
|
||||
multiple_results=True, boxed_storage=True)
|
||||
Return = InstructionFormat(VARIABLE_ARGS, boxed_storage=True)
|
||||
|
||||
ReturnReg = InstructionFormat(VALUE, VARIABLE_ARGS, boxed_storage=True)
|
||||
|
||||
# Finally extract the names of global variables in this module.
|
||||
InstructionFormat.extract_names(globals())
|
||||
|
||||
@@ -18,7 +18,7 @@ GROUP = InstructionGroup("base", "Shared base instruction set")
|
||||
|
||||
Int = TypeVar('Int', 'A scalar or vector integer type', ints=True, simd=True)
|
||||
iB = TypeVar('iB', 'A scalar integer type', ints=True)
|
||||
iPtr = TypeVar('iB', 'An integer address type', ints=(32, 64))
|
||||
iAddr = TypeVar('iAddr', 'An integer address type', ints=(32, 64))
|
||||
Testable = TypeVar(
|
||||
'Testable', 'A scalar boolean or integer type',
|
||||
ints=True, bools=True)
|
||||
@@ -113,6 +113,25 @@ x_return = Instruction(
|
||||
""",
|
||||
ins=rvals, is_terminator=True)
|
||||
|
||||
raddr = Operand('raddr', iAddr, doc='Return address')
|
||||
|
||||
return_reg = Instruction(
|
||||
'return_reg', r"""
|
||||
Return from the function to a return address held in a register.
|
||||
|
||||
Unconditionally transfer control to the calling function, passing the
|
||||
provided return values. The list of return values must match the
|
||||
function signature's return types.
|
||||
|
||||
This instruction should only be used by ISA-specific epilogue lowering
|
||||
code. It is equivalent to :inst:`return`, but the return address is
|
||||
provided explicitly in a register. This style of return instruction is
|
||||
used by RISC architectures such as ARM and RISC-V. A normal
|
||||
:inst:`return` will be legalized into this instruction on these
|
||||
architectures.
|
||||
""",
|
||||
ins=(raddr, rvals), is_terminator=True)
|
||||
|
||||
FN = Operand(
|
||||
'FN',
|
||||
entities.func_ref,
|
||||
@@ -130,7 +149,7 @@ call = Instruction(
|
||||
outs=rvals)
|
||||
|
||||
SIG = Operand('SIG', entities.sig_ref, doc='function signature')
|
||||
callee = Operand('callee', iPtr, doc='address of function to call')
|
||||
callee = Operand('callee', iAddr, doc='address of function to call')
|
||||
|
||||
call_indirect = Instruction(
|
||||
'call_indirect', r"""
|
||||
|
||||
@@ -222,6 +222,11 @@ pub enum InstructionData {
|
||||
ty: Type,
|
||||
data: Box<ReturnData>,
|
||||
},
|
||||
ReturnReg {
|
||||
opcode: Opcode,
|
||||
ty: Type,
|
||||
data: Box<ReturnRegData>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A variable list of `Value` operands used for function call arguments and passing arguments to
|
||||
@@ -406,6 +411,27 @@ pub struct ReturnData {
|
||||
pub varargs: VariableArgs,
|
||||
}
|
||||
|
||||
/// Payload of a return instruction.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ReturnRegData {
|
||||
/// Return address.
|
||||
pub arg: Value,
|
||||
/// Dynamically sized array containing return values.
|
||||
pub varargs: VariableArgs,
|
||||
}
|
||||
|
||||
impl ReturnRegData {
|
||||
/// Get references to the arguments.
|
||||
pub fn arguments(&self) -> [&[Value]; 2] {
|
||||
[ref_slice(&self.arg), &self.varargs]
|
||||
}
|
||||
|
||||
/// Get mutable references to the arguments.
|
||||
pub fn arguments_mut(&mut self) -> [&mut [Value]; 2] {
|
||||
[ref_slice_mut(&mut self.arg), &mut self.varargs]
|
||||
}
|
||||
}
|
||||
|
||||
/// Analyzing an instruction.
|
||||
///
|
||||
/// Avoid large matches on instruction formats by using the methods defined here to examine
|
||||
|
||||
@@ -239,6 +239,13 @@ fn write_instruction(w: &mut Write,
|
||||
writeln!(w, " {}", data.varargs)
|
||||
}
|
||||
}
|
||||
ReturnReg { ref data, .. } => {
|
||||
if data.varargs.is_empty() {
|
||||
writeln!(w, "{}", data.arg)
|
||||
} else {
|
||||
writeln!(w, "{}, {}", data.arg, data.varargs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ use cretonne::ir::immediates::{Imm64, Ieee32, Ieee64};
|
||||
use cretonne::ir::entities::AnyEntity;
|
||||
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs,
|
||||
TernaryOverflowData, JumpData, BranchData, CallData,
|
||||
IndirectCallData, ReturnData};
|
||||
IndirectCallData, ReturnData, ReturnRegData};
|
||||
use cretonne::isa;
|
||||
use cretonne::settings;
|
||||
use testfile::{TestFile, Details, Comment};
|
||||
@@ -197,6 +197,11 @@ impl Context {
|
||||
InstructionData::Return { ref mut data, .. } => {
|
||||
try!(self.map.rewrite_values(&mut data.varargs, loc));
|
||||
}
|
||||
|
||||
InstructionData::ReturnReg { ref mut data, .. } => {
|
||||
try!(self.map.rewrite_value(&mut data.arg, loc));
|
||||
try!(self.map.rewrite_values(&mut data.varargs, loc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1321,6 +1326,19 @@ impl<'a> Parser<'a> {
|
||||
data: Box::new(ReturnData { varargs: args }),
|
||||
}
|
||||
}
|
||||
InstructionFormat::ReturnReg => {
|
||||
let raddr = try!(self.match_value("expected SSA value return addr operand"));
|
||||
try!(self.match_token(Token::Comma, "expected ',' between operands"));
|
||||
let args = try!(self.parse_value_list());
|
||||
InstructionData::ReturnReg {
|
||||
opcode: opcode,
|
||||
ty: VOID,
|
||||
data: Box::new(ReturnRegData {
|
||||
arg: raddr,
|
||||
varargs: args,
|
||||
}),
|
||||
}
|
||||
}
|
||||
InstructionFormat::BranchTable => {
|
||||
let arg = try!(self.match_value("expected SSA value operand"));
|
||||
try!(self.match_token(Token::Comma, "expected ',' between operands"));
|
||||
|
||||
Reference in New Issue
Block a user