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:
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):

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
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

View File

@@ -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
}
}

View File

@@ -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);
}
}

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]),
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),
}
}

View File

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