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:
Jakob Stoklund Olesen
2017-02-21 13:05:17 -08:00
parent 74eb13c17a
commit 20ff2f0025
6 changed files with 75 additions and 4 deletions

View File

@@ -384,6 +384,7 @@ preamble`:
.. autoinst:: call .. autoinst:: call
.. autoinst:: x_return .. autoinst:: x_return
.. autoinst:: return_reg
This simple example illustrates direct function calls and signatures:: This simple example illustrates direct function calls and signatures::

View File

@@ -52,7 +52,7 @@ IndirectCall = InstructionFormat(
sig_ref, VALUE, VARIABLE_ARGS, sig_ref, VALUE, VARIABLE_ARGS,
multiple_results=True, boxed_storage=True) multiple_results=True, boxed_storage=True)
Return = InstructionFormat(VARIABLE_ARGS, 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. # Finally extract the names of global variables in this module.
InstructionFormat.extract_names(globals()) InstructionFormat.extract_names(globals())

View File

@@ -18,7 +18,7 @@ GROUP = InstructionGroup("base", "Shared base instruction set")
Int = TypeVar('Int', 'A scalar or vector integer type', ints=True, simd=True) Int = TypeVar('Int', 'A scalar or vector integer type', ints=True, simd=True)
iB = TypeVar('iB', 'A scalar integer type', ints=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 = TypeVar(
'Testable', 'A scalar boolean or integer type', 'Testable', 'A scalar boolean or integer type',
ints=True, bools=True) ints=True, bools=True)
@@ -113,6 +113,25 @@ x_return = Instruction(
""", """,
ins=rvals, is_terminator=True) 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 = Operand(
'FN', 'FN',
entities.func_ref, entities.func_ref,
@@ -130,7 +149,7 @@ call = Instruction(
outs=rvals) outs=rvals)
SIG = Operand('SIG', entities.sig_ref, doc='function signature') 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 = Instruction(
'call_indirect', r""" 'call_indirect', r"""

View File

@@ -222,6 +222,11 @@ pub enum InstructionData {
ty: Type, ty: Type,
data: Box<ReturnData>, 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 /// 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, 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. /// Analyzing an instruction.
/// ///
/// Avoid large matches on instruction formats by using the methods defined here to examine /// Avoid large matches on instruction formats by using the methods defined here to examine

View File

@@ -239,6 +239,13 @@ fn write_instruction(w: &mut Write,
writeln!(w, " {}", data.varargs) writeln!(w, " {}", data.varargs)
} }
} }
ReturnReg { ref data, .. } => {
if data.varargs.is_empty() {
writeln!(w, "{}", data.arg)
} else {
writeln!(w, "{}, {}", data.arg, data.varargs)
}
}
} }
} }

View File

@@ -16,7 +16,7 @@ use cretonne::ir::immediates::{Imm64, Ieee32, Ieee64};
use cretonne::ir::entities::AnyEntity; use cretonne::ir::entities::AnyEntity;
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs, use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs,
TernaryOverflowData, JumpData, BranchData, CallData, TernaryOverflowData, JumpData, BranchData, CallData,
IndirectCallData, ReturnData}; IndirectCallData, ReturnData, ReturnRegData};
use cretonne::isa; use cretonne::isa;
use cretonne::settings; use cretonne::settings;
use testfile::{TestFile, Details, Comment}; use testfile::{TestFile, Details, Comment};
@@ -197,6 +197,11 @@ impl Context {
InstructionData::Return { ref mut data, .. } => { InstructionData::Return { ref mut data, .. } => {
try!(self.map.rewrite_values(&mut data.varargs, loc)); 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 }), 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 => { InstructionFormat::BranchTable => {
let arg = try!(self.match_value("expected SSA value operand")); let arg = try!(self.match_value("expected SSA value operand"));
try!(self.match_token(Token::Comma, "expected ',' between operands")); try!(self.match_token(Token::Comma, "expected ',' between operands"));