use crate::Variable;
use core::mem;
use core::u32;
use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::entity::{EntityRef, PrimaryMap, SecondaryMap};
use cranelift_codegen::ir::immediates::{Ieee32, Ieee64};
use cranelift_codegen::ir::instructions::BranchInfo;
use cranelift_codegen::ir::types::{F32, F64};
use cranelift_codegen::ir::{Ebb, Function, Inst, InstBuilder, InstructionData, Type, Value};
use cranelift_codegen::packed_option::PackedOption;
use cranelift_codegen::packed_option::ReservedValue;
use std::vec::Vec;
pub struct SSABuilder {
variables: SecondaryMap<Variable, SecondaryMap<Block, PackedOption<Value>>>,
blocks: PrimaryMap<Block, BlockData>,
ebb_headers: SecondaryMap<Ebb, PackedOption<Block>>,
calls: Vec<Call>,
results: Vec<Value>,
side_effects: SideEffects,
}
pub struct SideEffects {
pub split_ebbs_created: Vec<Ebb>,
pub instructions_added_to_ebbs: Vec<Ebb>,
}
impl SideEffects {
fn new() -> Self {
Self {
split_ebbs_created: Vec::new(),
instructions_added_to_ebbs: Vec::new(),
}
}
fn is_empty(&self) -> bool {
self.split_ebbs_created.is_empty() && self.instructions_added_to_ebbs.is_empty()
}
}
enum BlockData {
EbbHeader(EbbHeaderBlockData),
EbbBody { predecessor: Block },
}
impl BlockData {
fn add_predecessor(&mut self, pred: Block, inst: Inst) {
match *self {
BlockData::EbbBody { .. } => panic!("you can't add a predecessor to a body block"),
BlockData::EbbHeader(ref mut data) => {
debug_assert!(!data.sealed, "sealed blocks cannot accept new predecessors");
data.predecessors.push(PredBlock::new(pred, inst));
}
}
}
fn remove_predecessor(&mut self, inst: Inst) -> Block {
match *self {
BlockData::EbbBody { .. } => panic!("should not happen"),
BlockData::EbbHeader(ref mut data) => {
let pred: usize = data
.predecessors
.iter()
.position(|&PredBlock { branch, .. }| branch == inst)
.expect("the predecessor you are trying to remove is not declared");
data.predecessors.swap_remove(pred).block
}
}
}
}
struct PredBlock {
block: Block,
branch: Inst,
}
impl PredBlock {
fn new(block: Block, branch: Inst) -> Self {
Self { block, branch }
}
}
struct EbbHeaderBlockData {
predecessors: Vec<PredBlock>,
sealed: bool,
ebb: Ebb,
undef_variables: Vec<(Variable, Value)>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Block(u32);
impl EntityRef for Block {
fn new(index: usize) -> Self {
debug_assert!(index < (u32::MAX as usize));
Block(index as u32)
}
fn index(self) -> usize {
self.0 as usize
}
}
impl ReservedValue for Block {
fn reserved_value() -> Self {
Block(u32::MAX)
}
}
impl SSABuilder {
pub fn new() -> Self {
Self {
variables: SecondaryMap::with_default(SecondaryMap::new()),
blocks: PrimaryMap::new(),
ebb_headers: SecondaryMap::new(),
calls: Vec::new(),
results: Vec::new(),
side_effects: SideEffects::new(),
}
}
pub fn clear(&mut self) {
self.variables.clear();
self.blocks.clear();
self.ebb_headers.clear();
debug_assert!(self.calls.is_empty());
debug_assert!(self.results.is_empty());
debug_assert!(self.side_effects.is_empty());
}
pub fn is_empty(&self) -> bool {
self.variables.is_empty()
&& self.blocks.is_empty()
&& self.ebb_headers.is_empty()
&& self.calls.is_empty()
&& self.results.is_empty()
&& self.side_effects.is_empty()
}
}
#[derive(Debug)]
enum ZeroOneOrMore<T> {
Zero,
One(T),
More,
}
#[derive(Debug)]
enum UseVarCases {
Unsealed(Value),
SealedOnePredecessor(Block),
SealedMultiplePredecessors(Value, Ebb),
}
enum Call {
UseVar(Block),
FinishSealedOnePredecessor(Block),
FinishPredecessorsLookup(Value, Ebb),
}
fn emit_zero(ty: Type, mut cur: FuncCursor) -> Value {
if ty.is_int() {
cur.ins().iconst(ty, 0)
} else if ty.is_bool() {
cur.ins().bconst(ty, false)
} else if ty == F32 {
cur.ins().f32const(Ieee32::with_bits(0))
} else if ty == F64 {
cur.ins().f64const(Ieee64::with_bits(0))
} else if ty.is_vector() {
let scalar_ty = ty.lane_type();
if scalar_ty.is_int() {
cur.ins().iconst(ty, 0)
} else if scalar_ty.is_bool() {
cur.ins().bconst(ty, false)
} else if scalar_ty == F32 {
let scalar = cur.ins().f32const(Ieee32::with_bits(0));
cur.ins().splat(ty, scalar)
} else if scalar_ty == F64 {
let scalar = cur.ins().f64const(Ieee64::with_bits(0));
cur.ins().splat(ty, scalar)
} else {
panic!("unimplemented scalar type: {:?}", ty)
}
} else {
panic!("unimplemented type: {:?}", ty)
}
}
impl SSABuilder {
pub fn def_var(&mut self, var: Variable, val: Value, block: Block) {
self.variables[var][block] = PackedOption::from(val);
}
pub fn use_var(
&mut self,
func: &mut Function,
var: Variable,
ty: Type,
block: Block,
) -> (Value, SideEffects) {
if let Some(var_defs) = self.variables.get(var) {
if let Some(val) = var_defs[block].expand() {
return (val, SideEffects::new());
}
}
debug_assert!(self.calls.is_empty());
debug_assert!(self.results.is_empty());
debug_assert!(self.side_effects.is_empty());
self.use_var_nonlocal(func, var, ty, block);
(
self.run_state_machine(func, var, ty),
mem::replace(&mut self.side_effects, SideEffects::new()),
)
}
fn use_var_nonlocal(&mut self, func: &mut Function, var: Variable, ty: Type, block: Block) {
let case = match self.blocks[block] {
BlockData::EbbHeader(ref mut data) => {
if data.sealed {
if data.predecessors.len() == 1 {
UseVarCases::SealedOnePredecessor(data.predecessors[0].block)
} else {
let val = func.dfg.append_ebb_param(data.ebb, ty);
UseVarCases::SealedMultiplePredecessors(val, data.ebb)
}
} else {
let val = func.dfg.append_ebb_param(data.ebb, ty);
data.undef_variables.push((var, val));
UseVarCases::Unsealed(val)
}
}
BlockData::EbbBody { predecessor: pred } => UseVarCases::SealedOnePredecessor(pred),
};
match case {
UseVarCases::SealedOnePredecessor(pred) => {
self.calls.push(Call::FinishSealedOnePredecessor(block));
self.calls.push(Call::UseVar(pred));
}
UseVarCases::Unsealed(val) => {
self.def_var(var, val, block);
self.results.push(val);
}
UseVarCases::SealedMultiplePredecessors(val, ebb) => {
self.def_var(var, val, block);
self.begin_predecessors_lookup(val, ebb);
}
}
}
fn finish_sealed_one_predecessor(&mut self, var: Variable, block: Block) {
let val = *self.results.last().unwrap();
self.def_var(var, val, block);
}
pub fn declare_ebb_body_block(&mut self, pred: Block) -> Block {
self.blocks.push(BlockData::EbbBody { predecessor: pred })
}
pub fn declare_ebb_header_block(&mut self, ebb: Ebb) -> Block {
let block = self.blocks.push(BlockData::EbbHeader(EbbHeaderBlockData {
predecessors: Vec::new(),
sealed: false,
ebb,
undef_variables: Vec::new(),
}));
self.ebb_headers[ebb] = block.into();
block
}
pub fn header_block(&self, ebb: Ebb) -> Block {
self.ebb_headers
.get(ebb)
.expect("the ebb has not been declared")
.expand()
.expect("the header block has not been defined")
}
pub fn declare_ebb_predecessor(&mut self, ebb: Ebb, pred: Block, inst: Inst) {
debug_assert!(!self.is_sealed(ebb));
let header_block = self.header_block(ebb);
self.blocks[header_block].add_predecessor(pred, inst)
}
pub fn remove_ebb_predecessor(&mut self, ebb: Ebb, inst: Inst) -> Block {
debug_assert!(!self.is_sealed(ebb));
let header_block = self.header_block(ebb);
self.blocks[header_block].remove_predecessor(inst)
}
pub fn seal_ebb_header_block(&mut self, ebb: Ebb, func: &mut Function) -> SideEffects {
self.seal_one_ebb_header_block(ebb, func);
mem::replace(&mut self.side_effects, SideEffects::new())
}
pub fn seal_all_ebb_header_blocks(&mut self, func: &mut Function) -> SideEffects {
for ebb in self.ebb_headers.keys() {
self.seal_one_ebb_header_block(ebb, func);
}
mem::replace(&mut self.side_effects, SideEffects::new())
}
fn seal_one_ebb_header_block(&mut self, ebb: Ebb, func: &mut Function) {
let block = self.header_block(ebb);
let undef_vars = match self.blocks[block] {
BlockData::EbbBody { .. } => panic!("this should not happen"),
BlockData::EbbHeader(ref mut data) => {
debug_assert!(
!data.sealed,
"Attempting to seal {} which is already sealed.",
ebb
);
debug_assert_eq!(ebb, data.ebb);
mem::replace(&mut data.undef_variables, Vec::new())
}
};
for (var, val) in undef_vars {
let ty = func.dfg.value_type(val);
self.predecessors_lookup(func, val, var, ty, ebb);
}
self.mark_ebb_header_block_sealed(block);
}
fn mark_ebb_header_block_sealed(&mut self, block: Block) {
match self.blocks[block] {
BlockData::EbbBody { .. } => panic!("this should not happen"),
BlockData::EbbHeader(ref mut data) => {
debug_assert!(!data.sealed);
debug_assert!(data.undef_variables.is_empty());
data.sealed = true;
}
}
}
fn predecessors_lookup(
&mut self,
func: &mut Function,
temp_arg_val: Value,
temp_arg_var: Variable,
ty: Type,
dest_ebb: Ebb,
) -> Value {
debug_assert!(self.calls.is_empty());
debug_assert!(self.results.is_empty());
self.begin_predecessors_lookup(temp_arg_val, dest_ebb);
self.run_state_machine(func, temp_arg_var, ty)
}
fn begin_predecessors_lookup(&mut self, temp_arg_val: Value, dest_ebb: Ebb) {
self.calls
.push(Call::FinishPredecessorsLookup(temp_arg_val, dest_ebb));
let mut calls = mem::replace(&mut self.calls, Vec::new());
calls.extend(
self.predecessors(dest_ebb)
.iter()
.rev()
.map(|&PredBlock { block: pred, .. }| Call::UseVar(pred)),
);
self.calls = calls;
}
fn finish_predecessors_lookup(
&mut self,
func: &mut Function,
temp_arg_val: Value,
temp_arg_var: Variable,
dest_ebb: Ebb,
) {
let mut pred_values: ZeroOneOrMore<Value> = ZeroOneOrMore::Zero;
for _ in 0..self.predecessors(dest_ebb).len() {
let pred_val = self.results.pop().unwrap();
match pred_values {
ZeroOneOrMore::Zero => {
if pred_val != temp_arg_val {
pred_values = ZeroOneOrMore::One(pred_val);
}
}
ZeroOneOrMore::One(old_val) => {
if pred_val != temp_arg_val && pred_val != old_val {
pred_values = ZeroOneOrMore::More;
}
}
ZeroOneOrMore::More => {}
}
}
let result_val = match pred_values {
ZeroOneOrMore::Zero => {
if !func.layout.is_ebb_inserted(dest_ebb) {
func.layout.append_ebb(dest_ebb);
}
self.side_effects.instructions_added_to_ebbs.push(dest_ebb);
let zero = emit_zero(
func.dfg.value_type(temp_arg_val),
FuncCursor::new(func).at_first_insertion_point(dest_ebb),
);
func.dfg.remove_ebb_param(temp_arg_val);
func.dfg.change_to_alias(temp_arg_val, zero);
zero
}
ZeroOneOrMore::One(pred_val) => {
let mut resolved = func.dfg.resolve_aliases(pred_val);
if temp_arg_val == resolved {
resolved = emit_zero(
func.dfg.value_type(temp_arg_val),
FuncCursor::new(func).at_first_insertion_point(dest_ebb),
);
}
func.dfg.remove_ebb_param(temp_arg_val);
func.dfg.change_to_alias(temp_arg_val, resolved);
resolved
}
ZeroOneOrMore::More => {
let mut preds = mem::replace(self.predecessors_mut(dest_ebb), Vec::new());
for &mut PredBlock {
block: ref mut pred_block,
branch: ref mut last_inst,
} in &mut preds
{
let pred_val = self
.variables
.get(temp_arg_var)
.unwrap()
.get(*pred_block)
.unwrap()
.unwrap();
let jump_arg = self.append_jump_argument(
func,
*last_inst,
*pred_block,
dest_ebb,
pred_val,
temp_arg_var,
);
if let Some((middle_ebb, middle_block, middle_jump_inst)) = jump_arg {
*pred_block = middle_block;
*last_inst = middle_jump_inst;
self.side_effects.split_ebbs_created.push(middle_ebb);
}
}
debug_assert!(self.predecessors(dest_ebb).is_empty());
*self.predecessors_mut(dest_ebb) = preds;
temp_arg_val
}
};
self.results.push(result_val);
}
fn append_jump_argument(
&mut self,
func: &mut Function,
jump_inst: Inst,
jump_inst_block: Block,
dest_ebb: Ebb,
val: Value,
var: Variable,
) -> Option<(Ebb, Block, Inst)> {
match func.dfg.analyze_branch(jump_inst) {
BranchInfo::NotABranch => {
panic!("you have declared a non-branch instruction as a predecessor to an ebb");
}
BranchInfo::SingleDest(_, _) => {
func.dfg.append_inst_arg(jump_inst, val);
None
}
BranchInfo::Table(jt, default_ebb) => {
let middle_ebb = func.dfg.make_ebb();
func.layout.append_ebb(middle_ebb);
let middle_block = self.declare_ebb_header_block(middle_ebb);
self.blocks[middle_block].add_predecessor(jump_inst_block, jump_inst);
self.mark_ebb_header_block_sealed(middle_block);
if let Some(default_ebb) = default_ebb {
if dest_ebb == default_ebb {
match func.dfg[jump_inst] {
InstructionData::BranchTable {
destination: ref mut dest,
..
} => {
*dest = middle_ebb;
}
_ => panic!("should not happen"),
}
}
}
for old_dest in func.jump_tables[jt].as_mut_slice() {
if *old_dest == dest_ebb {
*old_dest = middle_ebb;
}
}
let mut cur = FuncCursor::new(func).at_bottom(middle_ebb);
let middle_jump_inst = cur.ins().jump(dest_ebb, &[val]);
self.def_var(var, val, middle_block);
Some((middle_ebb, middle_block, middle_jump_inst))
}
}
}
fn predecessors(&self, ebb: Ebb) -> &[PredBlock] {
let block = self.header_block(ebb);
match self.blocks[block] {
BlockData::EbbBody { .. } => panic!("should not happen"),
BlockData::EbbHeader(ref data) => &data.predecessors,
}
}
pub fn has_any_predecessors(&self, ebb: Ebb) -> bool {
!self.predecessors(ebb).is_empty()
}
fn predecessors_mut(&mut self, ebb: Ebb) -> &mut Vec<PredBlock> {
let block = self.header_block(ebb);
match self.blocks[block] {
BlockData::EbbBody { .. } => panic!("should not happen"),
BlockData::EbbHeader(ref mut data) => &mut data.predecessors,
}
}
pub fn is_sealed(&self, ebb: Ebb) -> bool {
match self.blocks[self.header_block(ebb)] {
BlockData::EbbBody { .. } => panic!("should not happen"),
BlockData::EbbHeader(ref data) => data.sealed,
}
}
fn run_state_machine(&mut self, func: &mut Function, var: Variable, ty: Type) -> Value {
while let Some(call) = self.calls.pop() {
match call {
Call::UseVar(block) => {
if let Some(var_defs) = self.variables.get(var) {
if let Some(val) = var_defs[block].expand() {
self.results.push(val);
continue;
}
}
self.use_var_nonlocal(func, var, ty, block);
}
Call::FinishSealedOnePredecessor(block) => {
self.finish_sealed_one_predecessor(var, block);
}
Call::FinishPredecessorsLookup(temp_arg_val, dest_ebb) => {
self.finish_predecessors_lookup(func, temp_arg_val, var, dest_ebb);
}
}
}
debug_assert_eq!(self.results.len(), 1);
self.results.pop().unwrap()
}
}
#[cfg(test)]
mod tests {
use crate::ssa::SSABuilder;
use crate::Variable;
use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::instructions::BranchInfo;
use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{Function, Inst, InstBuilder, JumpTableData, Opcode};
use cranelift_codegen::settings;
use cranelift_codegen::verify_function;
#[test]
fn simple_block() {
let mut func = Function::new();
let mut ssa = SSABuilder::new();
let ebb0 = func.dfg.make_ebb();
let block = ssa.declare_ebb_header_block(ebb0);
let x_var = Variable::new(0);
let x_ssa = {
let mut cur = FuncCursor::new(&mut func);
cur.insert_ebb(ebb0);
cur.ins().iconst(I32, 1)
};
ssa.def_var(x_var, x_ssa, block);
let y_var = Variable::new(1);
let y_ssa = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iconst(I32, 2)
};
ssa.def_var(y_var, y_ssa, block);
assert_eq!(ssa.use_var(&mut func, x_var, I32, block).0, x_ssa);
assert_eq!(ssa.use_var(&mut func, y_var, I32, block).0, y_ssa);
let z_var = Variable::new(2);
let x_use1 = ssa.use_var(&mut func, x_var, I32, block).0;
let y_use1 = ssa.use_var(&mut func, y_var, I32, block).0;
let z1_ssa = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iadd(x_use1, y_use1)
};
ssa.def_var(z_var, z1_ssa, block);
assert_eq!(ssa.use_var(&mut func, z_var, I32, block).0, z1_ssa);
let x_use2 = ssa.use_var(&mut func, x_var, I32, block).0;
let z_use1 = ssa.use_var(&mut func, z_var, I32, block).0;
let z2_ssa = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iadd(x_use2, z_use1)
};
ssa.def_var(z_var, z2_ssa, block);
assert_eq!(ssa.use_var(&mut func, z_var, I32, block).0, z2_ssa);
}
#[test]
fn sequence_of_blocks() {
let mut func = Function::new();
let mut ssa = SSABuilder::new();
let ebb0 = func.dfg.make_ebb();
let ebb1 = func.dfg.make_ebb();
let block0 = ssa.declare_ebb_header_block(ebb0);
let x_var = Variable::new(0);
let x_ssa = {
let mut cur = FuncCursor::new(&mut func);
cur.insert_ebb(ebb0);
cur.insert_ebb(ebb1);
cur.goto_bottom(ebb0);
cur.ins().iconst(I32, 1)
};
ssa.def_var(x_var, x_ssa, block0);
let y_var = Variable::new(1);
let y_ssa = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iconst(I32, 2)
};
ssa.def_var(y_var, y_ssa, block0);
assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x_ssa);
assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y_ssa);
let z_var = Variable::new(2);
let x_use1 = ssa.use_var(&mut func, x_var, I32, block0).0;
let y_use1 = ssa.use_var(&mut func, y_var, I32, block0).0;
let z1_ssa = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iadd(x_use1, y_use1)
};
ssa.def_var(z_var, z1_ssa, block0);
assert_eq!(ssa.use_var(&mut func, z_var, I32, block0).0, z1_ssa);
let y_use2 = ssa.use_var(&mut func, y_var, I32, block0).0;
let jump_inst: Inst = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().brnz(y_use2, ebb1, &[])
};
let block1 = ssa.declare_ebb_body_block(block0);
let x_use2 = ssa.use_var(&mut func, x_var, I32, block1).0;
assert_eq!(x_use2, x_ssa);
let z_use1 = ssa.use_var(&mut func, z_var, I32, block1).0;
assert_eq!(z_use1, z1_ssa);
let z2_ssa = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iadd(x_use2, z_use1)
};
ssa.def_var(z_var, z2_ssa, block1);
assert_eq!(ssa.use_var(&mut func, z_var, I32, block1).0, z2_ssa);
ssa.seal_ebb_header_block(ebb0, &mut func);
let block2 = ssa.declare_ebb_header_block(ebb1);
ssa.declare_ebb_predecessor(ebb1, block0, jump_inst);
ssa.seal_ebb_header_block(ebb1, &mut func);
let x_use3 = ssa.use_var(&mut func, x_var, I32, block2).0;
assert_eq!(x_ssa, x_use3);
let y_use3 = ssa.use_var(&mut func, y_var, I32, block2).0;
assert_eq!(y_ssa, y_use3);
let y2_ssa = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iadd(x_use3, y_use3)
};
ssa.def_var(y_var, y2_ssa, block2);
match func.dfg.analyze_branch(jump_inst) {
BranchInfo::SingleDest(dest, jump_args) => {
assert_eq!(dest, ebb1);
assert_eq!(jump_args.len(), 0);
}
_ => assert!(false),
};
}
#[test]
fn program_with_loop() {
let mut func = Function::new();
let mut ssa = SSABuilder::new();
let ebb0 = func.dfg.make_ebb();
let ebb1 = func.dfg.make_ebb();
let ebb2 = func.dfg.make_ebb();
let block0 = ssa.declare_ebb_header_block(ebb0);
ssa.seal_ebb_header_block(ebb0, &mut func);
let x_var = Variable::new(0);
let x1 = {
let mut cur = FuncCursor::new(&mut func);
cur.insert_ebb(ebb0);
cur.insert_ebb(ebb1);
cur.insert_ebb(ebb2);
cur.goto_bottom(ebb0);
cur.ins().iconst(I32, 1)
};
ssa.def_var(x_var, x1, block0);
assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x1);
let y_var = Variable::new(1);
let y1 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iconst(I32, 2)
};
ssa.def_var(y_var, y1, block0);
assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y1);
let z_var = Variable::new(2);
let x2 = ssa.use_var(&mut func, x_var, I32, block0).0;
assert_eq!(x2, x1);
let y2 = ssa.use_var(&mut func, y_var, I32, block0).0;
assert_eq!(y2, y1);
let z1 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iadd(x2, y2)
};
ssa.def_var(z_var, z1, block0);
let jump_ebb0_ebb1 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().jump(ebb1, &[])
};
let block1 = ssa.declare_ebb_header_block(ebb1);
ssa.declare_ebb_predecessor(ebb1, block0, jump_ebb0_ebb1);
let z2 = ssa.use_var(&mut func, z_var, I32, block1).0;
let y3 = ssa.use_var(&mut func, y_var, I32, block1).0;
let z3 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
cur.ins().iadd(z2, y3)
};
ssa.def_var(z_var, z3, block1);
let y4 = ssa.use_var(&mut func, y_var, I32, block1).0;
assert_eq!(y4, y3);
let jump_ebb1_ebb2 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
cur.ins().brnz(y4, ebb2, &[])
};
let block2 = ssa.declare_ebb_body_block(block1);
let z4 = ssa.use_var(&mut func, z_var, I32, block2).0;
assert_eq!(z4, z3);
let x3 = ssa.use_var(&mut func, x_var, I32, block2).0;
let z5 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
cur.ins().isub(z4, x3)
};
ssa.def_var(z_var, z5, block2);
let y5 = ssa.use_var(&mut func, y_var, I32, block2).0;
assert_eq!(y5, y3);
{
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
cur.ins().return_(&[y5])
};
let block3 = ssa.declare_ebb_header_block(ebb2);
ssa.declare_ebb_predecessor(ebb2, block1, jump_ebb1_ebb2);
ssa.seal_ebb_header_block(ebb2, &mut func);
let y6 = ssa.use_var(&mut func, y_var, I32, block3).0;
assert_eq!(y6, y3);
let x4 = ssa.use_var(&mut func, x_var, I32, block3).0;
assert_eq!(x4, x3);
let y7 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb2);
cur.ins().isub(y6, x4)
};
ssa.def_var(y_var, y7, block3);
let jump_ebb2_ebb1 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb2);
cur.ins().jump(ebb1, &[])
};
ssa.declare_ebb_predecessor(ebb1, block3, jump_ebb2_ebb1);
ssa.seal_ebb_header_block(ebb1, &mut func);
assert_eq!(func.dfg.ebb_params(ebb1)[0], z2);
assert_eq!(func.dfg.ebb_params(ebb1)[1], y3);
assert_eq!(func.dfg.resolve_aliases(x3), x1);
}
#[test]
fn br_table_with_args() {
let mut func = Function::new();
let mut ssa = SSABuilder::new();
let mut jump_table = JumpTableData::new();
let ebb0 = func.dfg.make_ebb();
let ebb1 = func.dfg.make_ebb();
let ebb2 = func.dfg.make_ebb();
let block0 = ssa.declare_ebb_header_block(ebb0);
ssa.seal_ebb_header_block(ebb0, &mut func);
let x_var = Variable::new(0);
let x1 = {
let mut cur = FuncCursor::new(&mut func);
cur.insert_ebb(ebb0);
cur.insert_ebb(ebb1);
cur.insert_ebb(ebb2);
cur.goto_bottom(ebb0);
cur.ins().iconst(I32, 1)
};
ssa.def_var(x_var, x1, block0);
jump_table.push_entry(ebb2);
jump_table.push_entry(ebb1);
let jt = func.create_jump_table(jump_table);
ssa.use_var(&mut func, x_var, I32, block0).0;
let br_table = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().br_table(x1, ebb2, jt)
};
let block1 = ssa.declare_ebb_header_block(ebb1);
ssa.seal_ebb_header_block(ebb1, &mut func);
let x2 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
cur.ins().iconst(I32, 2)
};
ssa.def_var(x_var, x2, block1);
let jump_inst = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
cur.ins().jump(ebb2, &[])
};
let block3 = ssa.declare_ebb_header_block(ebb2);
ssa.declare_ebb_predecessor(ebb2, block1, jump_inst);
ssa.declare_ebb_predecessor(ebb2, block0, br_table);
ssa.seal_ebb_header_block(ebb2, &mut func);
let block4 = ssa.declare_ebb_body_block(block3);
let x3 = ssa.use_var(&mut func, x_var, I32, block4).0;
let x4 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb2);
cur.ins().iadd_imm(x3, 1)
};
ssa.def_var(x_var, x4, block4);
{
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb2);
cur.ins().return_(&[])
};
let flags = settings::Flags::new(settings::builder());
match verify_function(&func, &flags) {
Ok(()) => {}
Err(_errors) => {
#[cfg(feature = "std")]
panic!(_errors);
#[cfg(not(feature = "std"))]
panic!("function failed to verify");
}
}
}
#[test]
fn undef_values_reordering() {
let mut func = Function::new();
let mut ssa = SSABuilder::new();
let ebb0 = func.dfg.make_ebb();
let ebb1 = func.dfg.make_ebb();
let block0 = ssa.declare_ebb_header_block(ebb0);
let x_var = Variable::new(0);
let y_var = Variable::new(1);
let z_var = Variable::new(2);
ssa.seal_ebb_header_block(ebb0, &mut func);
let x1 = {
let mut cur = FuncCursor::new(&mut func);
cur.insert_ebb(ebb0);
cur.insert_ebb(ebb1);
cur.goto_bottom(ebb0);
cur.ins().iconst(I32, 0)
};
ssa.def_var(x_var, x1, block0);
let y1 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iconst(I32, 1)
};
ssa.def_var(y_var, y1, block0);
let z1 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().iconst(I32, 2)
};
ssa.def_var(z_var, z1, block0);
let jump_inst = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb0);
cur.ins().jump(ebb1, &[])
};
let block1 = ssa.declare_ebb_header_block(ebb1);
ssa.declare_ebb_predecessor(ebb1, block0, jump_inst);
let z2 = ssa.use_var(&mut func, z_var, I32, block1).0;
assert_eq!(func.dfg.ebb_params(ebb1)[0], z2);
let x2 = ssa.use_var(&mut func, x_var, I32, block1).0;
assert_eq!(func.dfg.ebb_params(ebb1)[1], x2);
let x3 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
cur.ins().iadd(x2, z2)
};
ssa.def_var(x_var, x3, block1);
let x4 = ssa.use_var(&mut func, x_var, I32, block1).0;
let y3 = ssa.use_var(&mut func, y_var, I32, block1).0;
assert_eq!(func.dfg.ebb_params(ebb1)[2], y3);
let y4 = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
cur.ins().isub(y3, x4)
};
ssa.def_var(y_var, y4, block1);
let jump_inst = {
let mut cur = FuncCursor::new(&mut func).at_bottom(ebb1);
cur.ins().jump(ebb1, &[])
};
ssa.declare_ebb_predecessor(ebb1, block1, jump_inst);
ssa.seal_ebb_header_block(ebb1, &mut func);
assert_eq!(func.dfg.ebb_params(ebb1)[1], y3);
assert_eq!(func.dfg.ebb_params(ebb1)[0], x2);
}
#[test]
fn undef() {
let mut func = Function::new();
let mut ssa = SSABuilder::new();
let ebb0 = func.dfg.make_ebb();
let block = ssa.declare_ebb_header_block(ebb0);
ssa.seal_ebb_header_block(ebb0, &mut func);
let i32_var = Variable::new(0);
let f32_var = Variable::new(1);
let f64_var = Variable::new(2);
let b1_var = Variable::new(3);
let f32x4_var = Variable::new(4);
ssa.use_var(&mut func, i32_var, I32, block);
ssa.use_var(&mut func, f32_var, F32, block);
ssa.use_var(&mut func, f64_var, F64, block);
ssa.use_var(&mut func, b1_var, B1, block);
ssa.use_var(&mut func, f32x4_var, F32X4, block);
assert_eq!(func.dfg.num_ebb_params(ebb0), 0);
}
#[test]
fn undef_in_entry() {
let mut func = Function::new();
let mut ssa = SSABuilder::new();
let ebb0 = func.dfg.make_ebb();
let block = ssa.declare_ebb_header_block(ebb0);
ssa.seal_ebb_header_block(ebb0, &mut func);
let x_var = Variable::new(0);
assert_eq!(func.dfg.num_ebb_params(ebb0), 0);
ssa.use_var(&mut func, x_var, I32, block);
assert_eq!(func.dfg.num_ebb_params(ebb0), 0);
assert_eq!(
func.dfg[func.layout.first_inst(ebb0).unwrap()].opcode(),
Opcode::Iconst
);
}
#[test]
fn undef_in_entry_sealed_after() {
let mut func = Function::new();
let mut ssa = SSABuilder::new();
let ebb0 = func.dfg.make_ebb();
let block = ssa.declare_ebb_header_block(ebb0);
let x_var = Variable::new(0);
assert_eq!(func.dfg.num_ebb_params(ebb0), 0);
ssa.use_var(&mut func, x_var, I32, block);
assert_eq!(func.dfg.num_ebb_params(ebb0), 1);
ssa.seal_ebb_header_block(ebb0, &mut func);
assert_eq!(func.dfg.num_ebb_params(ebb0), 0);
assert_eq!(
func.dfg[func.layout.first_inst(ebb0).unwrap()].opcode(),
Opcode::Iconst
);
}
#[test]
fn unreachable_use() {
let mut func = Function::new();
let mut ssa = SSABuilder::new();
let ebb0 = func.dfg.make_ebb();
let ebb1 = func.dfg.make_ebb();
let _block0 = ssa.declare_ebb_header_block(ebb0);
ssa.seal_ebb_header_block(ebb0, &mut func);
let block1 = ssa.declare_ebb_header_block(ebb1);
let block2 = ssa.declare_ebb_body_block(block1);
{
let mut cur = FuncCursor::new(&mut func);
cur.insert_ebb(ebb0);
cur.insert_ebb(ebb1);
cur.goto_bottom(ebb0);
cur.ins().return_(&[]);
let x_var = Variable::new(0);
cur.goto_bottom(ebb1);
let val = ssa.use_var(&mut cur.func, x_var, I32, block1).0;
let brz = cur.ins().brz(val, ebb1, &[]);
ssa.declare_ebb_predecessor(ebb1, block1, brz);
let j = cur.ins().jump(ebb1, &[]);
ssa.declare_ebb_predecessor(ebb1, block2, j);
}
ssa.seal_ebb_header_block(ebb1, &mut func);
let flags = settings::Flags::new(settings::builder());
match verify_function(&func, &flags) {
Ok(()) => {}
Err(_errors) => {
#[cfg(feature = "std")]
panic!(_errors);
#[cfg(not(feature = "std"))]
panic!("function failed to verify");
}
}
}
#[test]
fn unreachable_use_with_multiple_preds() {
let mut func = Function::new();
let mut ssa = SSABuilder::new();
let ebb0 = func.dfg.make_ebb();
let ebb1 = func.dfg.make_ebb();
let ebb2 = func.dfg.make_ebb();
let _block0 = ssa.declare_ebb_header_block(ebb0);
ssa.seal_ebb_header_block(ebb0, &mut func);
let block1 = ssa.declare_ebb_header_block(ebb1);
let block2 = ssa.declare_ebb_header_block(ebb2);
{
let mut cur = FuncCursor::new(&mut func);
let x_var = Variable::new(0);
cur.insert_ebb(ebb0);
cur.insert_ebb(ebb1);
cur.insert_ebb(ebb2);
cur.goto_bottom(ebb0);
cur.ins().return_(&[]);
cur.goto_bottom(ebb1);
let v = ssa.use_var(&mut cur.func, x_var, I32, block1).0;
let brz = cur.ins().brz(v, ebb2, &[]);
let j0 = cur.ins().jump(ebb1, &[]);
cur.goto_bottom(ebb2);
let j1 = cur.ins().jump(ebb1, &[]);
ssa.declare_ebb_predecessor(ebb1, block2, brz);
ssa.declare_ebb_predecessor(ebb1, block1, j0);
ssa.declare_ebb_predecessor(ebb2, block1, j1);
}
ssa.seal_ebb_header_block(ebb1, &mut func);
ssa.seal_ebb_header_block(ebb2, &mut func);
let flags = settings::Flags::new(settings::builder());
match verify_function(&func, &flags) {
Ok(()) => {}
Err(_errors) => {
#[cfg(feature = "std")]
panic!(_errors);
#[cfg(not(feature = "std"))]
panic!("function failed to verify");
}
}
}
}