Define control flow instructions.
Rename 'br' to 'jump'. We'll use jump/br to mean unconditional/conditional control transfer respectively.
This commit is contained in:
@@ -262,7 +262,12 @@ class InstDocumenter(sphinx.ext.autodoc.Documenter):
|
||||
if len(inst.outs) > 0:
|
||||
sig = ', '.join([op.name for op in inst.outs]) + ' = ' + sig
|
||||
if len(inst.ins) > 0:
|
||||
sig = sig + ' ' + ', '.join([op.name for op in inst.ins])
|
||||
sig = sig + ' ' + inst.ins[0].name
|
||||
for op in inst.ins[1:]:
|
||||
if op.typ.operand_kind().name == 'variable_args':
|
||||
sig += '({}...)'.format(op.name)
|
||||
else:
|
||||
sig += ', ' + op.name
|
||||
return sig
|
||||
|
||||
def add_directive_header(self, sig):
|
||||
|
||||
@@ -313,56 +313,10 @@ arguments, if it has any. Conditional branches only take the branch if their
|
||||
condition is satisfied, otherwise execution continues at the following
|
||||
instruction in the EBB.
|
||||
|
||||
.. inst:: br EBB(args...)
|
||||
|
||||
Branch.
|
||||
|
||||
Unconditionally branch to an extended basic block, passing the specified
|
||||
EBB arguments. The number and types of arguments must match the destination
|
||||
EBB.
|
||||
|
||||
:arg EBB: Destination extended basic block.
|
||||
:arg args...: Zero or more arguments passed to EBB.
|
||||
:result: None. This is a terminator instruction.
|
||||
|
||||
.. inst:: brz x, EBB(args...)
|
||||
|
||||
Branch when zero.
|
||||
|
||||
If ``x`` is a :type:`b1` value, take the branch when ``x`` is false. If
|
||||
``x`` is an integer value, take the branch when ``x = 0``.
|
||||
|
||||
:arg Testable x: Value to test.
|
||||
:arg EBB: Destination extended basic block.
|
||||
:arg args...: Arguments passed to EBB.
|
||||
:result: None.
|
||||
|
||||
.. inst:: brnz x, EBB(args...)
|
||||
|
||||
Branch when non-zero.
|
||||
|
||||
If ``x`` is a :type:`b1` value, take the branch when ``x`` is true. If
|
||||
``x`` is an integer value, take the branch when ``x != 0``.
|
||||
|
||||
:arg Testable x: Value to test.
|
||||
:arg EBB: Destination extended basic block.
|
||||
:arg args...: Zero or more arguments passed to EBB.
|
||||
:result: None.
|
||||
|
||||
.. inst:: br_table x, JT
|
||||
|
||||
Jump table branch.
|
||||
|
||||
Use ``x`` as an unsigned index into the jump table ``JT``. If a jump table
|
||||
entry is found, branch to the corresponding EBB. If no entry was found fall
|
||||
through to the next instruction.
|
||||
|
||||
Note that this branch instruction can't pass arguments to the targeted
|
||||
blocks. Split critical edges as needed to work around this.
|
||||
|
||||
:arg iN x: Integer index into jump table.
|
||||
:arg JT: Jump table which was declared in the preamble.
|
||||
:result: None.
|
||||
.. autoinst:: jump
|
||||
.. autoinst:: brz
|
||||
.. autoinst:: brnz
|
||||
.. autoinst:: br_table
|
||||
|
||||
.. inst:: JT = jump_table EBB0, EBB1, ..., EBBn
|
||||
|
||||
@@ -386,29 +340,9 @@ explicit trap instructions defined below, but some instructions may also cause
|
||||
traps for certain input value. For example, :inst:`udiv` traps when the divisor
|
||||
is zero.
|
||||
|
||||
.. inst:: trap
|
||||
|
||||
Terminate execution unconditionally.
|
||||
|
||||
:result: None. This is a terminator instruction.
|
||||
|
||||
.. inst:: trapz x
|
||||
|
||||
Trap when zero.
|
||||
|
||||
if ``x`` is non-zero, execution continues at the following instruction.
|
||||
|
||||
:arg Testable x: Value to test.
|
||||
:result: None.
|
||||
|
||||
.. inst:: trapnz x
|
||||
|
||||
Trap when non-zero.
|
||||
|
||||
if ``x`` is zero, execution continues at the following instruction.
|
||||
|
||||
:arg Testable x: Value to test.
|
||||
:result: None.
|
||||
.. autoinst:: trap
|
||||
.. autoinst:: trapz
|
||||
.. autoinst:: trapnz
|
||||
|
||||
|
||||
Function calls
|
||||
|
||||
@@ -450,6 +450,8 @@ class Instruction(object):
|
||||
operands and other operand kinds.
|
||||
:param outs: Tuple of output operands. The output operands must be SSA
|
||||
values.
|
||||
:param is_terminator: This is a terminator instruction.
|
||||
:param is_branch: This is a branch instruction.
|
||||
"""
|
||||
|
||||
def __init__(self, name, doc, ins=(), outs=(), **kwargs):
|
||||
|
||||
@@ -4,18 +4,94 @@ Cretonne base instruction set.
|
||||
This module defines the basic Cretonne instruction set that all targets
|
||||
support.
|
||||
"""
|
||||
from . import TypeVar, Operand, Instruction, InstructionGroup
|
||||
from . import TypeVar, Operand, Instruction, InstructionGroup, variable_args
|
||||
from types import i8, f32, f64
|
||||
from immediates import imm64, ieee32, ieee64, immvector
|
||||
import entities
|
||||
|
||||
instructions = 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)
|
||||
Testable = TypeVar(
|
||||
'Testable', 'A scalar boolean or integer type',
|
||||
ints=True, bools=True)
|
||||
TxN = TypeVar(
|
||||
'%Tx%N', 'A SIMD vector type',
|
||||
ints=True, floats=True, bools=True, scalars=False, simd=True)
|
||||
|
||||
#
|
||||
# Control flow
|
||||
#
|
||||
c = Operand('c', Testable, doc='Controlling value to test')
|
||||
EBB = Operand('EBB', entities.ebb, doc='Destination extended basic block')
|
||||
args = Operand('args', variable_args, doc='EBB arguments')
|
||||
|
||||
jump = Instruction(
|
||||
'jump', r"""
|
||||
Jump.
|
||||
|
||||
Unconditionally jump to an extended basic block, passing the specified
|
||||
EBB arguments. The number and types of arguments must match the
|
||||
destination EBB.
|
||||
""",
|
||||
ins=(EBB, args), is_terminator=True)
|
||||
|
||||
brz = Instruction(
|
||||
'brz', r"""
|
||||
Branch when zero.
|
||||
|
||||
If ``c`` is a :type:`b1` value, take the branch when ``c`` is false. If
|
||||
``c`` is an integer value, take the branch when ``c = 0``.
|
||||
""",
|
||||
ins=(c, EBB, args), is_branch=True)
|
||||
|
||||
brnz = Instruction(
|
||||
'brnz', r"""
|
||||
Branch when non-zero.
|
||||
|
||||
If ``c`` is a :type:`b1` value, take the branch when ``c`` is true. If
|
||||
``c`` is an integer value, take the branch when ``c != 0``.
|
||||
""",
|
||||
ins=(c, EBB, args), is_branch=True)
|
||||
|
||||
x = Operand('x', iB, doc='index into jump table')
|
||||
JT = Operand('JT', entities.jump_table)
|
||||
br_table = Instruction(
|
||||
'br_table', r"""
|
||||
Indirect branch via jump table.
|
||||
|
||||
Use ``x`` as an unsigned index into the jump table ``JT``. If a jump table
|
||||
entry is found, branch to the corresponding EBB. If no entry was found fall
|
||||
through to the next instruction.
|
||||
|
||||
Note that this branch instruction can't pass arguments to the targeted
|
||||
blocks. Split critical edges as needed to work around this.
|
||||
""",
|
||||
ins=(x, JT), is_branch=True)
|
||||
|
||||
trap = Instruction(
|
||||
'trap', r"""
|
||||
Terminate execution unconditionally.
|
||||
""",
|
||||
is_terminator=True)
|
||||
|
||||
trapz = Instruction(
|
||||
'trapz', r"""
|
||||
Trap when zero.
|
||||
|
||||
if ``c`` is non-zero, execution continues at the following instruction.
|
||||
""",
|
||||
ins=c)
|
||||
|
||||
trapnz = Instruction(
|
||||
'trapnz', r"""
|
||||
Trap when non-zero.
|
||||
|
||||
if ``c`` is zero, execution continues at the following instruction.
|
||||
""",
|
||||
ins=c)
|
||||
|
||||
#
|
||||
# Materializing constants.
|
||||
#
|
||||
|
||||
@@ -21,3 +21,6 @@ signature = EntityRefKind('signature', 'A function signature.')
|
||||
#: A reference to an external function declared in the function preamble.
|
||||
#: This is used to provide the callee and signature in a call instruction.
|
||||
function = EntityRefKind('function', 'An external function.')
|
||||
|
||||
#: A reference to a jump table declared in the function preamble.
|
||||
jump_table = EntityRefKind('jump_table', 'A jump table.')
|
||||
|
||||
@@ -9,7 +9,7 @@ in this module.
|
||||
|
||||
from . import InstructionFormat, value, variable_args
|
||||
from immediates import imm64, ieee32, ieee64, immvector
|
||||
from entities import function
|
||||
from entities import ebb, function, jump_table
|
||||
|
||||
Nullary = InstructionFormat()
|
||||
|
||||
@@ -23,6 +23,10 @@ Binary = InstructionFormat(value, value)
|
||||
BinaryImm = InstructionFormat(value, imm64)
|
||||
BinaryImmRev = InstructionFormat(imm64, value)
|
||||
|
||||
Jump = InstructionFormat(ebb, variable_args)
|
||||
Branch = InstructionFormat(value, ebb, variable_args)
|
||||
BranchTable = InstructionFormat(value, jump_table)
|
||||
|
||||
Call = InstructionFormat(function, variable_args, multiple_results=True)
|
||||
|
||||
# Finally extract the names of global variables in this module.
|
||||
|
||||
@@ -189,3 +189,34 @@ impl Default for StackSlot {
|
||||
NO_STACK_SLOT
|
||||
}
|
||||
}
|
||||
|
||||
/// An opaque reference to a jump table.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct JumpTable(u32);
|
||||
|
||||
impl JumpTable {
|
||||
pub fn new(index: usize) -> JumpTable {
|
||||
assert!(index < (u32::MAX as usize));
|
||||
JumpTable(index as u32)
|
||||
}
|
||||
|
||||
pub fn index(&self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Display a `JumpTable` reference as "jt12".
|
||||
impl Display for JumpTable {
|
||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||
write!(fmt, "jt{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A guaranteed invalid jump table reference.
|
||||
pub const NO_JUMP_TABLE: JumpTable = JumpTable(u32::MAX);
|
||||
|
||||
impl Default for JumpTable {
|
||||
fn default() -> JumpTable {
|
||||
NO_JUMP_TABLE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +140,22 @@ pub enum InstructionData {
|
||||
rhs: Value,
|
||||
lhs: Imm64,
|
||||
},
|
||||
Jump {
|
||||
opcode: Opcode,
|
||||
ty: Type,
|
||||
data: Box<JumpData>,
|
||||
},
|
||||
Branch {
|
||||
opcode: Opcode,
|
||||
ty: Type,
|
||||
data: Box<BranchData>,
|
||||
},
|
||||
BranchTable {
|
||||
opcode: Opcode,
|
||||
ty: Type,
|
||||
arg: Value,
|
||||
table: JumpTable,
|
||||
},
|
||||
Call {
|
||||
opcode: Opcode,
|
||||
ty: Type,
|
||||
@@ -147,6 +163,66 @@ pub enum InstructionData {
|
||||
},
|
||||
}
|
||||
|
||||
/// A variable list of `Value` operands used for function call arguments and passing arguments to
|
||||
/// basic blocks.
|
||||
#[derive(Debug)]
|
||||
pub struct VariableArgs(Vec<Value>);
|
||||
|
||||
impl VariableArgs {
|
||||
pub fn new() -> VariableArgs {
|
||||
VariableArgs(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for VariableArgs {
|
||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||
try!(write!(fmt, "("));
|
||||
for (i, val) in self.0.iter().enumerate() {
|
||||
if i == 0 {
|
||||
try!(write!(fmt, "{}", val));
|
||||
} else {
|
||||
try!(write!(fmt, ", {}", val));
|
||||
}
|
||||
}
|
||||
write!(fmt, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VariableArgs {
|
||||
fn default() -> VariableArgs {
|
||||
VariableArgs::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Payload data for jump instructions. These need to carry lists of EBB arguments that won't fit
|
||||
/// in the allowed InstructionData size.
|
||||
#[derive(Debug)]
|
||||
pub struct JumpData {
|
||||
destination: Ebb,
|
||||
arguments: VariableArgs,
|
||||
}
|
||||
|
||||
impl Display for JumpData {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}{}", self.destination, self.arguments)
|
||||
}
|
||||
}
|
||||
|
||||
/// Payload data for branch instructions. These need to carry lists of EBB arguments that won't fit
|
||||
/// in the allowed InstructionData size.
|
||||
#[derive(Debug)]
|
||||
pub struct BranchData {
|
||||
arg: Value,
|
||||
destination: Ebb,
|
||||
arguments: VariableArgs,
|
||||
}
|
||||
|
||||
impl Display for BranchData {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}, {}{}", self.arg, self.destination, self.arguments)
|
||||
}
|
||||
}
|
||||
|
||||
/// Payload of a call instruction.
|
||||
#[derive(Debug)]
|
||||
pub struct CallData {
|
||||
@@ -154,9 +230,14 @@ pub struct CallData {
|
||||
second_result: Value,
|
||||
|
||||
// Dynamically sized array containing call argument values.
|
||||
arguments: Vec<Value>,
|
||||
arguments: VariableArgs,
|
||||
}
|
||||
|
||||
impl Display for CallData {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "TBD{}", self.arguments)
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionData {
|
||||
/// Create data for a call instruction.
|
||||
@@ -166,7 +247,7 @@ impl InstructionData {
|
||||
ty: return_type,
|
||||
data: Box::new(CallData {
|
||||
second_result: NO_VALUE,
|
||||
arguments: Vec::new(),
|
||||
arguments: VariableArgs::new(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -184,6 +265,9 @@ impl InstructionData {
|
||||
Binary { opcode, .. } => opcode,
|
||||
BinaryImm { opcode, .. } => opcode,
|
||||
BinaryImmRev { opcode, .. } => opcode,
|
||||
Jump { opcode, .. } => opcode,
|
||||
Branch { opcode, .. } => opcode,
|
||||
BranchTable { opcode, .. } => opcode,
|
||||
Call { opcode, .. } => opcode,
|
||||
}
|
||||
}
|
||||
@@ -201,6 +285,9 @@ impl InstructionData {
|
||||
Binary { ty, .. } => ty,
|
||||
BinaryImm { ty, .. } => ty,
|
||||
BinaryImmRev { ty, .. } => ty,
|
||||
Jump { ty, .. } => ty,
|
||||
Branch { ty, .. } => ty,
|
||||
BranchTable { ty, .. } => ty,
|
||||
Call { ty, .. } => ty,
|
||||
}
|
||||
}
|
||||
@@ -218,6 +305,9 @@ impl InstructionData {
|
||||
Binary { .. } => None,
|
||||
BinaryImm { .. } => None,
|
||||
BinaryImmRev { .. } => None,
|
||||
Jump { .. } => None,
|
||||
Branch { .. } => None,
|
||||
BranchTable { .. } => None,
|
||||
Call { ref data, .. } => Some(data.second_result),
|
||||
}
|
||||
}
|
||||
@@ -234,6 +324,9 @@ impl InstructionData {
|
||||
Binary { .. } => None,
|
||||
BinaryImm { .. } => None,
|
||||
BinaryImmRev { .. } => None,
|
||||
Jump { .. } => None,
|
||||
Branch { .. } => None,
|
||||
BranchTable { .. } => None,
|
||||
Call { ref mut data, .. } => Some(&mut data.second_result),
|
||||
}
|
||||
}
|
||||
@@ -263,4 +356,15 @@ mod tests {
|
||||
assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
|
||||
assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instruction_data() {
|
||||
use std::mem;
|
||||
// The size of the InstructionData enum is important for performance. It should not exceed
|
||||
// 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that require
|
||||
// more space than that.
|
||||
// It would be fine with a data structure smaller than 16 bytes, but what are the odds of
|
||||
// that?
|
||||
assert_eq!(mem::size_of::<InstructionData>(), 16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,10 @@ pub fn write_instruction(w: &mut Write, func: &Function, inst: Inst) -> Result {
|
||||
Binary { opcode, args, .. } => writeln!(w, "{} {}, {}", opcode, args[0], args[1]),
|
||||
BinaryImm { opcode, lhs, rhs, .. } => writeln!(w, "{} {}, {}", opcode, lhs, rhs),
|
||||
BinaryImmRev { opcode, lhs, rhs, .. } => writeln!(w, "{} {}, {}", opcode, lhs, rhs),
|
||||
Call { opcode, .. } => writeln!(w, "{} [...]", opcode),
|
||||
Jump { opcode, ref data, .. } => writeln!(w, "{} {}", opcode, data),
|
||||
Branch { opcode, ref data, .. } => writeln!(w, "{} {}", opcode, data),
|
||||
BranchTable { opcode, arg, table, .. } => writeln!(w, "{} {}, {}", opcode, arg, table),
|
||||
Call { opcode, ref data, .. } => writeln!(w, "{} {}", opcode, data),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -657,6 +657,9 @@ impl<'a> Parser<'a> {
|
||||
rhs: rhs,
|
||||
}
|
||||
}
|
||||
InstructionFormat::Jump |
|
||||
InstructionFormat::Branch |
|
||||
InstructionFormat::BranchTable |
|
||||
InstructionFormat::Call => {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user