diff --git a/docs/langref.rst b/docs/langref.rst index 5bd59b18d3..d7bffd870a 100644 --- a/docs/langref.rst +++ b/docs/langref.rst @@ -384,6 +384,7 @@ preamble`: .. autoinst:: call .. autoinst:: x_return +.. autoinst:: return_reg This simple example illustrates direct function calls and signatures:: diff --git a/lib/cretonne/meta/base/formats.py b/lib/cretonne/meta/base/formats.py index 421913b0e4..89704623fd 100644 --- a/lib/cretonne/meta/base/formats.py +++ b/lib/cretonne/meta/base/formats.py @@ -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()) diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index eb98d175ef..1693763468 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/lib/cretonne/meta/base/instructions.py @@ -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""" diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index 17a0ef2c27..d778ddb9a3 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -222,6 +222,11 @@ pub enum InstructionData { ty: Type, data: Box, }, + ReturnReg { + opcode: Opcode, + ty: Type, + data: Box, + }, } /// 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 diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index 8dfe955e42..7a328a6d30 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -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) + } + } } } diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 9decd562b0..8651e09d4d 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -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"));