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:
Jakob Stoklund Olesen
2016-05-18 16:28:21 -07:00
parent 27a311701d
commit b06f5ef72f
10 changed files with 244 additions and 79 deletions

View File

@@ -262,7 +262,12 @@ class InstDocumenter(sphinx.ext.autodoc.Documenter):
if len(inst.outs) > 0: if len(inst.outs) > 0:
sig = ', '.join([op.name for op in inst.outs]) + ' = ' + sig sig = ', '.join([op.name for op in inst.outs]) + ' = ' + sig
if len(inst.ins) > 0: 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 return sig
def add_directive_header(self, sig): def add_directive_header(self, sig):

View File

@@ -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 condition is satisfied, otherwise execution continues at the following
instruction in the EBB. instruction in the EBB.
.. inst:: br EBB(args...) .. autoinst:: jump
.. autoinst:: brz
Branch. .. autoinst:: brnz
.. autoinst:: br_table
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.
.. inst:: JT = jump_table EBB0, EBB1, ..., EBBn .. 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 traps for certain input value. For example, :inst:`udiv` traps when the divisor
is zero. is zero.
.. inst:: trap .. autoinst:: trap
.. autoinst:: trapz
Terminate execution unconditionally. .. autoinst:: trapnz
: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.
Function calls Function calls

View File

@@ -189,3 +189,34 @@ impl Default for StackSlot {
NO_STACK_SLOT 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
}
}

View File

@@ -140,6 +140,22 @@ pub enum InstructionData {
rhs: Value, rhs: Value,
lhs: Imm64, 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 { Call {
opcode: Opcode, opcode: Opcode,
ty: Type, 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. /// Payload of a call instruction.
#[derive(Debug)] #[derive(Debug)]
pub struct CallData { pub struct CallData {
@@ -154,9 +230,14 @@ pub struct CallData {
second_result: Value, second_result: Value,
// Dynamically sized array containing call argument values. // 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 { impl InstructionData {
/// Create data for a call instruction. /// Create data for a call instruction.
@@ -166,7 +247,7 @@ impl InstructionData {
ty: return_type, ty: return_type,
data: Box::new(CallData { data: Box::new(CallData {
second_result: NO_VALUE, second_result: NO_VALUE,
arguments: Vec::new(), arguments: VariableArgs::new(),
}), }),
} }
} }
@@ -184,6 +265,9 @@ impl InstructionData {
Binary { opcode, .. } => opcode, Binary { opcode, .. } => opcode,
BinaryImm { opcode, .. } => opcode, BinaryImm { opcode, .. } => opcode,
BinaryImmRev { opcode, .. } => opcode, BinaryImmRev { opcode, .. } => opcode,
Jump { opcode, .. } => opcode,
Branch { opcode, .. } => opcode,
BranchTable { opcode, .. } => opcode,
Call { opcode, .. } => opcode, Call { opcode, .. } => opcode,
} }
} }
@@ -201,6 +285,9 @@ impl InstructionData {
Binary { ty, .. } => ty, Binary { ty, .. } => ty,
BinaryImm { ty, .. } => ty, BinaryImm { ty, .. } => ty,
BinaryImmRev { ty, .. } => ty, BinaryImmRev { ty, .. } => ty,
Jump { ty, .. } => ty,
Branch { ty, .. } => ty,
BranchTable { ty, .. } => ty,
Call { ty, .. } => ty, Call { ty, .. } => ty,
} }
} }
@@ -218,6 +305,9 @@ impl InstructionData {
Binary { .. } => None, Binary { .. } => None,
BinaryImm { .. } => None, BinaryImm { .. } => None,
BinaryImmRev { .. } => None, BinaryImmRev { .. } => None,
Jump { .. } => None,
Branch { .. } => None,
BranchTable { .. } => None,
Call { ref data, .. } => Some(data.second_result), Call { ref data, .. } => Some(data.second_result),
} }
} }
@@ -234,6 +324,9 @@ impl InstructionData {
Binary { .. } => None, Binary { .. } => None,
BinaryImm { .. } => None, BinaryImm { .. } => None,
BinaryImmRev { .. } => None, BinaryImmRev { .. } => None,
Jump { .. } => None,
Branch { .. } => None,
BranchTable { .. } => None,
Call { ref mut data, .. } => Some(&mut data.second_result), Call { ref mut data, .. } => Some(&mut data.second_result),
} }
} }
@@ -263,4 +356,15 @@ mod tests {
assert_eq!("".parse::<Opcode>(), Err("Unknown opcode")); assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
assert_eq!("\0".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);
}
} }

View File

@@ -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]), Binary { opcode, args, .. } => writeln!(w, "{} {}, {}", opcode, args[0], args[1]),
BinaryImm { opcode, lhs, rhs, .. } => writeln!(w, "{} {}, {}", opcode, lhs, rhs), BinaryImm { opcode, lhs, rhs, .. } => writeln!(w, "{} {}, {}", opcode, lhs, rhs),
BinaryImmRev { 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),
} }
} }

View File

@@ -657,6 +657,9 @@ impl<'a> Parser<'a> {
rhs: rhs, rhs: rhs,
} }
} }
InstructionFormat::Jump |
InstructionFormat::Branch |
InstructionFormat::BranchTable |
InstructionFormat::Call => { InstructionFormat::Call => {
unimplemented!(); unimplemented!();
} }

View File

@@ -450,6 +450,8 @@ class Instruction(object):
operands and other operand kinds. operands and other operand kinds.
:param outs: Tuple of output operands. The output operands must be SSA :param outs: Tuple of output operands. The output operands must be SSA
values. 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): def __init__(self, name, doc, ins=(), outs=(), **kwargs):

View File

@@ -4,18 +4,94 @@ Cretonne base instruction set.
This module defines the basic Cretonne instruction set that all targets This module defines the basic Cretonne instruction set that all targets
support. support.
""" """
from . import TypeVar, Operand, Instruction, InstructionGroup from . import TypeVar, Operand, Instruction, InstructionGroup, variable_args
from types import i8, f32, f64 from types import i8, f32, f64
from immediates import imm64, ieee32, ieee64, immvector from immediates import imm64, ieee32, ieee64, immvector
import entities
instructions = InstructionGroup("base", "Shared base instruction set") instructions = 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)
Testable = TypeVar(
'Testable', 'A scalar boolean or integer type',
ints=True, bools=True)
TxN = TypeVar( TxN = TypeVar(
'%Tx%N', 'A SIMD vector type', '%Tx%N', 'A SIMD vector type',
ints=True, floats=True, bools=True, scalars=False, simd=True) 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. # Materializing constants.
# #

View File

@@ -21,3 +21,6 @@ signature = EntityRefKind('signature', 'A function signature.')
#: A reference to an external function declared in the function preamble. #: A reference to an external function declared in the function preamble.
#: This is used to provide the callee and signature in a call instruction. #: This is used to provide the callee and signature in a call instruction.
function = EntityRefKind('function', 'An external function.') 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.')

View File

@@ -9,7 +9,7 @@ in this module.
from . import InstructionFormat, value, variable_args from . import InstructionFormat, value, variable_args
from immediates import imm64, ieee32, ieee64, immvector from immediates import imm64, ieee32, ieee64, immvector
from entities import function from entities import ebb, function, jump_table
Nullary = InstructionFormat() Nullary = InstructionFormat()
@@ -23,6 +23,10 @@ Binary = InstructionFormat(value, value)
BinaryImm = InstructionFormat(value, imm64) BinaryImm = InstructionFormat(value, imm64)
BinaryImmRev = InstructionFormat(imm64, value) 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) Call = InstructionFormat(function, variable_args, multiple_results=True)
# Finally extract the names of global variables in this module. # Finally extract the names of global variables in this module.