Introduce Keep and DropKeep structs in isa

This commit is contained in:
Sergey Pepyakin 2018-06-18 19:30:11 +03:00
parent 0f6da829de
commit 29002153ec
5 changed files with 181 additions and 172 deletions

View File

@ -40,11 +40,23 @@
//! - Reserved immediates are ignored for `call_indirect`, `current_memory`, `grow_memory`.
//!
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Keep {
None,
Single,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct DropKeep {
pub drop: u32,
pub keep: Keep,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Target {
pub dst_pc: u32,
pub drop: u32,
pub keep: u8,
pub drop_keep: DropKeep,
}
#[allow(unused)] // TODO: Remove
@ -71,10 +83,7 @@ pub enum Instruction {
BrTable(Box<[Target]>),
Unreachable,
Return {
drop: u32,
keep: u8,
},
Return(DropKeep),
Call(u32),
CallIndirect(u32),

View File

@ -33,7 +33,7 @@ pub enum InstructionOutcome {
/// Execute function call.
ExecuteCall(FuncRef),
/// Return from current function block.
Return(u32, u8),
Return(isa::DropKeep),
}
/// Function run result.
@ -157,14 +157,14 @@ impl<'a, E: Externals> Interpreter<'a, E> {
InstructionOutcome::RunNextInstruction => function_context.position += 1,
InstructionOutcome::Branch(target) => {
function_context.position = target.dst_pc as usize;
self.value_stack.drop_keep(target.drop, target.keep);
self.value_stack.drop_keep(target.drop_keep);
},
InstructionOutcome::ExecuteCall(func_ref) => {
function_context.position += 1;
return Ok(RunResult::NestedCall(func_ref));
},
InstructionOutcome::Return(drop, keep) => {
self.value_stack.drop_keep(drop, keep);
InstructionOutcome::Return(drop_keep) => {
self.value_stack.drop_keep(drop_keep);
break;
},
}
@ -182,7 +182,7 @@ impl<'a, E: Externals> Interpreter<'a, E> {
&isa::Instruction::BrIfEqz(ref target) => self.run_br_eqz(target.clone()),
&isa::Instruction::BrIfNez(ref target) => self.run_br_nez(target.clone()),
&isa::Instruction::BrTable(ref targets) => self.run_br_table(targets),
&isa::Instruction::Return { drop, keep } => self.run_return(drop, keep),
&isa::Instruction::Return(drop_keep) => self.run_return(drop_keep),
&isa::Instruction::Call(index) => self.run_call(context, index),
&isa::Instruction::CallIndirect(index) => self.run_call_indirect(context, index),
@ -405,8 +405,8 @@ impl<'a, E: Externals> Interpreter<'a, E> {
Ok(InstructionOutcome::Branch(dst))
}
fn run_return(&mut self, drop: u32, keep: u8) -> Result<InstructionOutcome, TrapKind> {
Ok(InstructionOutcome::Return(drop, keep))
fn run_return(&mut self, drop_keep: isa::DropKeep) -> Result<InstructionOutcome, TrapKind> {
Ok(InstructionOutcome::Return(drop_keep))
}
fn run_call(
@ -1127,16 +1127,14 @@ impl ValueStack {
}
#[inline]
fn drop_keep(&mut self, drop: u32, keep: u8) {
assert!(keep <= 1);
if keep == 1 {
fn drop_keep(&mut self, drop_keep: isa::DropKeep) {
if drop_keep.keep == isa::Keep::Single {
let top = *self.top();
*self.pick_mut(drop as usize + 1) = top;
*self.pick_mut(drop_keep.drop as usize + 1) = top;
}
let cur_stack_len = self.len();
self.sp = cur_stack_len - drop as usize;
self.sp = cur_stack_len - drop_keep.drop as usize;
}
#[inline]

View File

@ -283,7 +283,7 @@ impl Validator {
context.sink.emit_br_eqz(Target {
label: if_not,
drop_keep: DropKeep { drop: 0, keep: 0 },
drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, },
});
}
Else => {
@ -303,7 +303,7 @@ impl Validator {
// to the "end_label" (it will be resolved at End).
context.sink.emit_br(Target {
label: end_label,
drop_keep: DropKeep { drop: 0, keep: 0 },
drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, },
});
// Resolve `if_not` to here so when if condition is unsatisfied control flow
@ -375,15 +375,12 @@ impl Validator {
)?;
}
let DropKeep { drop, keep } = drop_keep_return(
let drop_keep = drop_keep_return(
&context.locals,
&context.value_stack,
&context.frame_stack,
);
context.sink.emit(isa::Instruction::Return {
drop,
keep,
});
context.sink.emit(isa::Instruction::Return(drop_keep));
}
pop_label(&mut context.value_stack, &mut context.frame_stack)?;
@ -441,15 +438,12 @@ impl Validator {
tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?;
}
let DropKeep { drop, keep } = drop_keep_return(
let drop_keep = drop_keep_return(
&context.locals,
&context.value_stack,
&context.frame_stack
);
context.sink.emit(isa::Instruction::Return {
drop,
keep,
});
context.sink.emit(isa::Instruction::Return(drop_keep));
return Ok(InstructionOutcome::Unreachable);
}
@ -1549,13 +1543,13 @@ fn require_target(
require_label(depth, frame_stack).expect("require_target called with a bogus depth");
// Find out how many values we need to keep (copy to the new stack location after the drop).
let keep: u8 = match (frame.frame_type, frame.block_type) {
let keep: isa::Keep = match (frame.frame_type, frame.block_type) {
// A loop doesn't take a value upon a branch. It can return value
// only via reaching it's closing `End` operator.
(BlockFrameType::Loop { .. }, _) => 0,
(BlockFrameType::Loop { .. }, _) => isa::Keep::None,
(_, BlockType::Value(_)) => 1,
(_, BlockType::NoResult) => 0,
(_, BlockType::Value(_)) => isa::Keep::Single,
(_, BlockType::NoResult) => isa::Keep::None,
};
// Find out how many values we need to discard.
@ -1573,7 +1567,7 @@ fn require_target(
);
assert!(
(value_stack_height as u32 - frame.value_stack_len as u32) >= keep as u32,
"Stack underflow detected: asked to keep {} values, but there are only {}",
"Stack underflow detected: asked to keep {:?} values, but there are only {}",
keep,
value_stack_height as u32 - frame.value_stack_len as u32,
);
@ -1582,7 +1576,7 @@ fn require_target(
Target {
label: frame.frame_type.br_destination(),
drop_keep: DropKeep { drop, keep },
drop_keep: isa::DropKeep { drop, keep },
}
}
@ -1590,7 +1584,7 @@ fn drop_keep_return(
locals: &Locals,
value_stack: &StackWithLimit<StackValueType>,
frame_stack: &StackWithLimit<BlockFrame>,
) -> DropKeep {
) -> isa::DropKeep {
assert!(
!frame_stack.is_empty(),
"drop_keep_return can't be called with the frame stack empty"
@ -1627,16 +1621,10 @@ fn relative_local_depth(
Ok(depth)
}
#[derive(Clone)]
struct DropKeep {
drop: u32,
keep: u8,
}
#[derive(Clone)]
struct Target {
label: LabelId,
drop_keep: DropKeep,
drop_keep: isa::DropKeep,
}
#[derive(Debug)]
@ -1694,51 +1682,39 @@ impl Sink {
fn emit_br(&mut self, target: Target) {
let Target {
label,
drop_keep: DropKeep {
drop,
keep,
},
drop_keep,
} = target;
let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc });
self.ins.push(isa::Instruction::Br(isa::Target {
dst_pc,
drop,
keep,
drop_keep: drop_keep.into(),
}));
}
fn emit_br_eqz(&mut self, target: Target) {
let Target {
label,
drop_keep: DropKeep {
drop,
keep,
},
drop_keep,
} = target;
let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc });
self.ins.push(isa::Instruction::BrIfEqz(isa::Target {
dst_pc,
drop,
keep,
drop_keep: drop_keep.into(),
}));
}
fn emit_br_nez(&mut self, target: Target) {
let Target {
label,
drop_keep: DropKeep {
drop,
keep,
},
drop_keep,
} = target;
let pc = self.cur_pc();
let dst_pc = self.pc_or_placeholder(label, || Reloc::Br { pc });
self.ins.push(isa::Instruction::BrIfNez(isa::Target {
dst_pc,
drop,
keep,
drop_keep: drop_keep.into(),
}));
}
@ -1747,16 +1723,12 @@ impl Sink {
let pc = self.cur_pc();
let mut isa_targets = Vec::new();
for (idx, &Target { label, drop_keep: DropKeep {
drop,
keep,
}}) in targets.iter().chain(iter::once(&default)).enumerate() {
for (idx, &Target { label, drop_keep }) in targets.iter().chain(iter::once(&default)).enumerate() {
let dst_pc = self.pc_or_placeholder(label, || Reloc::BrTable { pc, idx });
isa_targets.push(
isa::Target {
dst_pc,
keep,
drop,
drop_keep: drop_keep.into(),
},
);
}

View File

@ -327,10 +327,10 @@ fn implicit_return_no_value() {
assert_eq!(
code,
vec![
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 0,
keep: 0,
}
keep: isa::Keep::None,
})
]
)
}
@ -348,10 +348,10 @@ fn implicit_return_with_value() {
code,
vec![
isa::Instruction::I32Const(0),
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 0,
keep: 1,
}
keep: isa::Keep::Single,
}),
]
)
}
@ -367,10 +367,10 @@ fn implicit_return_param() {
assert_eq!(
code,
vec![
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 1,
keep: 0,
}
keep: isa::Keep::None,
}),
]
)
}
@ -388,10 +388,10 @@ fn get_local() {
code,
vec![
isa::Instruction::GetLocal(1),
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 1,
keep: 1,
}
keep: isa::Keep::Single,
}),
]
)
}
@ -410,14 +410,14 @@ fn explicit_return() {
code,
vec![
isa::Instruction::GetLocal(1),
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 1,
keep: 1,
},
isa::Instruction::Return {
keep: isa::Keep::Single,
}),
isa::Instruction::Return(isa::DropKeep {
drop: 1,
keep: 1,
}
keep: isa::Keep::Single,
}),
]
)
}
@ -444,10 +444,10 @@ fn add_params() {
isa::Instruction::GetLocal(2),
isa::Instruction::GetLocal(2),
isa::Instruction::I32Add,
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 2,
keep: 1,
}
keep: isa::Keep::Single,
}),
]
)
}
@ -468,10 +468,10 @@ fn drop_locals() {
vec![
isa::Instruction::GetLocal(2),
isa::Instruction::SetLocal(1),
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 2,
keep: 0,
}
keep: isa::Keep::None,
}),
]
)
}
@ -496,19 +496,21 @@ fn if_without_else() {
isa::Instruction::I32Const(1),
isa::Instruction::BrIfEqz(isa::Target {
dst_pc: 4,
drop: 0,
keep: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(2),
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 1, // 1 param
keep: 1, // 1 result
},
keep: isa::Keep::Single, // 1 result
}),
isa::Instruction::I32Const(3),
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 1,
keep: 1,
},
keep: isa::Keep::Single,
}),
]
)
}
@ -536,22 +538,26 @@ fn if_else() {
isa::Instruction::I32Const(1),
isa::Instruction::BrIfEqz(isa::Target {
dst_pc: 5,
drop: 0,
keep: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(2),
isa::Instruction::SetLocal(1),
isa::Instruction::Br(isa::Target {
dst_pc: 7,
drop: 0,
keep: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(3),
isa::Instruction::SetLocal(1),
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 1,
keep: 0,
},
keep: isa::Keep::None,
}),
]
)
}
@ -577,21 +583,25 @@ fn if_else_returns_result() {
isa::Instruction::I32Const(1),
isa::Instruction::BrIfEqz(isa::Target {
dst_pc: 4,
drop: 0,
keep: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(2),
isa::Instruction::Br(isa::Target {
dst_pc: 5,
drop: 0,
keep: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(3),
isa::Instruction::Drop,
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 0,
keep: 0,
},
keep: isa::Keep::None,
}),
]
)
}
@ -621,29 +631,35 @@ fn if_else_branch_from_true_branch() {
isa::Instruction::I32Const(1),
isa::Instruction::BrIfEqz(isa::Target {
dst_pc: 8,
drop: 0,
keep: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(1),
isa::Instruction::I32Const(1),
isa::Instruction::BrIfNez(isa::Target {
dst_pc: 9,
drop: 0,
keep: 1,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::Single,
},
}),
isa::Instruction::Drop,
isa::Instruction::I32Const(2),
isa::Instruction::Br(isa::Target {
dst_pc: 9,
drop: 0,
keep: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(3),
isa::Instruction::Drop,
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 0,
keep: 0,
},
keep: isa::Keep::None,
}),
]
)
}
@ -673,29 +689,35 @@ fn if_else_branch_from_false_branch() {
isa::Instruction::I32Const(1),
isa::Instruction::BrIfEqz(isa::Target {
dst_pc: 4,
drop: 0,
keep: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(1),
isa::Instruction::Br(isa::Target {
dst_pc: 9,
drop: 0,
keep: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(2),
isa::Instruction::I32Const(1),
isa::Instruction::BrIfNez(isa::Target {
dst_pc: 9,
drop: 0,
keep: 1,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::Single,
},
}),
isa::Instruction::Drop,
isa::Instruction::I32Const(3),
isa::Instruction::Drop,
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 0,
keep: 0,
},
keep: isa::Keep::None,
}),
]
)
}
@ -720,15 +742,17 @@ fn loop_() {
isa::Instruction::I32Const(1),
isa::Instruction::BrIfNez(isa::Target {
dst_pc: 0,
drop: 0,
keep: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(2),
isa::Instruction::Drop,
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 0,
keep: 0,
},
keep: isa::Keep::None,
}),
]
)
}
@ -746,10 +770,10 @@ fn loop_empty() {
assert_eq!(
code,
vec![
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 0,
keep: 0,
},
keep: isa::Keep::None,
}),
]
)
}
@ -776,20 +800,24 @@ fn brtable() {
vec![
isa::Target {
dst_pc: 0,
keep: 0,
drop: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
},
isa::Target {
dst_pc: 2,
keep: 0,
drop: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
},
].into_boxed_slice()
),
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 0,
keep: 0,
},
keep: isa::Keep::None,
}),
]
)
}
@ -820,22 +848,26 @@ fn brtable_returns_result() {
vec![
isa::Target {
dst_pc: 3,
keep: 1,
drop: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::Single,
},
},
isa::Target {
dst_pc: 4,
keep: 1,
drop: 0,
drop_keep: isa::DropKeep {
keep: isa::Keep::Single,
drop: 0,
},
},
].into_boxed_slice()
),
isa::Instruction::Unreachable,
isa::Instruction::Drop,
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 0,
keep: 0,
},
keep: isa::Keep::None,
}),
]
)
}
@ -862,24 +894,25 @@ fn wabt_example() {
isa::Instruction::GetLocal(1),
isa::Instruction::BrIfNez(isa::Target {
dst_pc: 4,
keep: 0,
drop: 0,
drop_keep: isa::DropKeep {
drop: 0,
keep: isa::Keep::None,
},
}),
isa::Instruction::I32Const(1),
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 1, // 1 parameter
keep: 1, // return value
},
keep: isa::Keep::Single,
}),
isa::Instruction::I32Const(2),
isa::Instruction::Return {
isa::Instruction::Return(isa::DropKeep {
drop: 1,
keep: 1,
},
isa::Instruction::Return {
keep: isa::Keep::Single,
}),
isa::Instruction::Return(isa::DropKeep {
drop: 1,
keep: 1,
},
keep: isa::Keep::Single,
}),
]
)
}

View File

@ -123,12 +123,9 @@ mod tests {
Local::new(u32::max_value(), ValueType::I32),
Local::new(1, ValueType::I64),
];
let locals = Locals::new(&[], &local_groups).unwrap();
assert_matches!(
locals.type_of_local(u32::max_value() - 1),
Ok(ValueType::I32)
Locals::new(&[], &local_groups),
Err(_)
);
assert_matches!(locals.type_of_local(u32::max_value()), Err(_));
}
}