Fix branching out of loops and improve tracking

This commit is contained in:
Rerumu 2022-06-15 02:34:02 -04:00
parent a6cf4fdf07
commit 941bb4842b

View File

@ -82,6 +82,24 @@ impl<'a> TypeInfo<'a> {
self.arity_of(*adjusted) self.arity_of(*adjusted)
} }
fn block_arity_of(&self, typ: BlockType) -> Arity {
match typ {
BlockType::NoResult => Arity {
num_param: 0,
num_result: 0,
},
BlockType::Value(_) => Arity {
num_param: 0,
num_result: 1,
},
BlockType::TypeIndex(i) => {
let id = i.try_into().unwrap();
self.arity_of(id)
}
}
}
fn func_of_import(import: &ImportEntry) -> Option<usize> { fn func_of_import(import: &ImportEntry) -> Option<usize> {
if let &External::Function(i) = import.external() { if let &External::Function(i) = import.external() {
Some(i.try_into().unwrap()) Some(i.try_into().unwrap())
@ -110,17 +128,35 @@ impl<'a> TypeInfo<'a> {
} }
} }
enum BlockVariant {
Forward,
Backward,
If,
Else,
}
enum BlockData {
Forward { num_result: usize },
Backward { num_param: usize },
If { num_result: usize, typ: BlockType },
Else { num_result: usize },
}
impl Default for BlockData {
fn default() -> Self {
BlockData::Forward { num_result: 0 }
}
}
#[derive(Default)] #[derive(Default)]
struct StatList { struct StatList {
stack: Vec<Expression>, stack: Vec<Expression>,
code: Vec<Statement>, code: Vec<Statement>,
last: Option<Terminator>, last: Option<Terminator>,
num_result: usize, block_data: BlockData,
num_param: usize,
num_stack: usize, num_stack: usize,
num_previous: usize, num_previous: usize,
is_else: bool,
} }
impl StatList { impl StatList {
@ -376,26 +412,29 @@ impl<'a> Builder<'a> {
} }
} }
fn start_block(&mut self, typ: BlockType, stat: Statement) { fn start_block(&mut self, typ: BlockType, variant: BlockVariant) {
let (num_param, num_result) = match typ { let Arity {
BlockType::NoResult => (0, 0), num_param,
BlockType::Value(_) => (0, 1), num_result,
BlockType::TypeIndex(i) => { } = self.type_info.block_arity_of(typ);
let id = i.try_into().unwrap();
let arity = self.type_info.arity_of(id);
(arity.num_param, arity.num_result)
}
};
let mut old = std::mem::take(&mut self.target); let mut old = std::mem::take(&mut self.target);
old.leak_all(); old.leak_all();
old.code.push(stat);
self.target.block_data = match variant {
BlockVariant::Forward => BlockData::Forward { num_result },
BlockVariant::Backward => BlockData::Backward { num_param },
BlockVariant::If => BlockData::If { num_result, typ },
BlockVariant::Else => {
old.pop_len(num_result);
old.push_temporary(num_param);
BlockData::Else { num_result }
}
};
self.target.stack = old.pop_len(num_param); self.target.stack = old.pop_len(num_param);
self.target.num_result = num_result;
self.target.num_param = num_param;
self.target.num_previous = old.num_previous + old.stack.len(); self.target.num_previous = old.num_previous + old.stack.len();
old.push_temporary(num_result); old.push_temporary(num_result);
@ -404,22 +443,15 @@ impl<'a> Builder<'a> {
} }
fn start_else(&mut self) { fn start_else(&mut self) {
let mut temp = StatList { let typ = if let BlockData::If { typ, .. } = self.target.block_data {
num_result: self.target.num_result, typ
num_param: self.target.num_param, } else {
num_stack: self.target.num_stack, unreachable!()
num_previous: self.target.num_previous,
is_else: true,
..Default::default()
}; };
temp.push_temporary(temp.num_param); self.target.leak_all();
self.end_block(); self.end_block();
self.start_block(typ, BlockVariant::Else);
let old = std::mem::replace(&mut self.target, temp);
self.pending.push(old);
} }
fn end_block(&mut self) { fn end_block(&mut self) {
@ -428,13 +460,26 @@ impl<'a> Builder<'a> {
self.target.num_stack = now.num_stack; self.target.num_stack = now.num_stack;
match self.target.code.last_mut().unwrap() { let stat = match now.block_data {
Statement::Forward(data) => *data = now.into(), BlockData::Forward { .. } => Statement::Forward(now.into()),
Statement::Backward(data) => *data = now.into(), BlockData::Backward { .. } => Statement::Backward(now.into()),
Statement::If(data) if !now.is_else => data.truthy = now.into(), BlockData::If { .. } => Statement::If(If {
Statement::If(data) if now.is_else => data.falsey = Some(now.into()), cond: self.target.pop_required(),
_ => unreachable!(), truthy: now.into(),
} falsey: None,
}),
BlockData::Else { .. } => {
if let Statement::If(v) = self.target.code.last_mut().unwrap() {
v.falsey = Some(now.into());
} else {
unreachable!()
}
return;
}
};
self.target.code.push(stat);
} }
fn get_relative_block(&self, index: usize) -> Option<&StatList> { fn get_relative_block(&self, index: usize) -> Option<&StatList> {
@ -447,7 +492,15 @@ impl<'a> Builder<'a> {
fn get_br_terminator(&self, target: usize) -> Br { fn get_br_terminator(&self, target: usize) -> Br {
let (par_start, par_result) = match self.get_relative_block(target) { let (par_start, par_result) = match self.get_relative_block(target) {
Some(v) => (v.num_previous, v.num_result), Some(v) => (
v.num_previous,
match v.block_data {
BlockData::Forward { num_result } => num_result,
BlockData::Backward { num_param } => num_param,
BlockData::If { num_result, .. } => num_result,
BlockData::Else { num_result } => num_result,
},
),
None => (0, self.num_result), None => (0, self.num_result),
}; };
@ -534,26 +587,18 @@ impl<'a> Builder<'a> {
} }
Inst::Nop => {} Inst::Nop => {}
Inst::Block(typ) => { Inst::Block(typ) => {
let stat = Statement::Forward(Forward::default()); self.start_block(typ, BlockVariant::Forward);
self.start_block(typ, stat);
} }
Inst::Loop(typ) => { Inst::Loop(typ) => {
let stat = Statement::Backward(Backward::default()); self.start_block(typ, BlockVariant::Backward);
self.start_block(typ, stat);
} }
Inst::If(typ) => { Inst::If(typ) => {
let stat = Statement::If(If { let cond = self.target.pop_required();
cond: self.target.pop_required(),
truthy: Forward::default(),
falsey: None,
});
self.start_block(typ, stat); self.start_block(typ, BlockVariant::If);
self.pending.last_mut().unwrap().stack.push(cond);
} }
Inst::Else => { Inst::Else => {
self.target.leak_all();
self.start_else(); self.start_else();
} }
Inst::End => { Inst::End => {
@ -726,7 +771,7 @@ impl<'a> Builder<'a> {
fn build_stat_list(&mut self, list: &[Instruction], num_result: usize) -> StatList { fn build_stat_list(&mut self, list: &[Instruction], num_result: usize) -> StatList {
self.nested_unreachable = 0; self.nested_unreachable = 0;
self.num_result = num_result; self.num_result = num_result;
self.target.num_result = num_result; self.target.block_data = BlockData::Forward { num_result };
for inst in list.iter().take(list.len() - 1) { for inst in list.iter().take(list.len() - 1) {
if self.nested_unreachable == 0 { if self.nested_unreachable == 0 {