Model polymorphic stack explicitly

This commit is contained in:
Sergey Pepyakin 2018-02-07 14:48:11 +03:00
parent fdd527e996
commit 842ff25aaf
3 changed files with 68 additions and 70 deletions

View File

@ -26,6 +26,10 @@ pub struct BlockFrame {
pub end_position: usize, pub end_position: usize,
/// A limit integer value, which is an index into the value stack indicating where to reset it to on a branch to that label. /// A limit integer value, which is an index into the value stack indicating where to reset it to on a branch to that label.
pub value_stack_len: usize, pub value_stack_len: usize,
/// Boolean which signals whether value stack became polymorphic. Value stack starts in non-polymorphic state and
/// becomes polymorphic only after an instruction that never passes control further is executed,
/// i.e. `unreachable`, `br` (but not `br_if`!), etc.
pub polymorphic_stack: bool,
} }
/// Type of block frame. /// Type of block frame.

View File

@ -1153,6 +1153,7 @@ impl FunctionContext {
branch_position: branch_position, branch_position: branch_position,
end_position: end_position, end_position: end_position,
value_stack_len: self.value_stack.len(), value_stack_len: self.value_stack.len(),
polymorphic_stack: false,
}).map_err(|_| TrapKind::StackOverflow)?; }).map_err(|_| TrapKind::StackOverflow)?;
Ok(()) Ok(())

View File

@ -41,8 +41,6 @@ struct FunctionValidationContext<'a> {
enum StackValueType { enum StackValueType {
/// Any value type. /// Any value type.
Any, Any,
/// Any number of any values of any type.
AnyUnlimited,
/// Concrete value type. /// Concrete value type.
Specific(ValueType), Specific(ValueType),
} }
@ -347,13 +345,13 @@ impl Validator {
} }
fn validate_drop(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> { fn validate_drop(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.pop_any_value().map(|_| ())?; context.pop_value(StackValueType::Any).map(|_| ())?;
Ok(InstructionOutcome::ValidateNextInstruction) Ok(InstructionOutcome::ValidateNextInstruction)
} }
fn validate_select(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> { fn validate_select(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.pop_value(ValueType::I32.into())?; context.pop_value(ValueType::I32.into())?;
let select_type = context.pop_any_value()?; let select_type = context.pop_value(StackValueType::Any)?;
context.pop_value(select_type)?; context.pop_value(select_type)?;
context.push_value(select_type)?; context.push_value(select_type)?;
Ok(InstructionOutcome::ValidateNextInstruction) Ok(InstructionOutcome::ValidateNextInstruction)
@ -367,7 +365,7 @@ impl Validator {
fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> { fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?; let local_type = context.require_local(index)?;
let value_type = context.pop_any_value()?; let value_type = context.pop_value(StackValueType::Any)?;
if local_type != value_type { if local_type != value_type {
return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type))); return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type)));
} }
@ -376,7 +374,7 @@ impl Validator {
fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> { fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result<InstructionOutcome, Error> {
let local_type = context.require_local(index)?; let local_type = context.require_local(index)?;
let value_type = context.tee_any_value()?; let value_type = context.tee_value(StackValueType::Any)?;
if local_type != value_type { if local_type != value_type {
return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type))); return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type)));
} }
@ -397,7 +395,7 @@ impl Validator {
let global = context.module.require_global(index, Some(true))?; let global = context.module.require_global(index, Some(true))?;
global.content_type().into() global.content_type().into()
}; };
let value_type = context.pop_any_value()?; let value_type = context.pop_value(StackValueType::Any)?;
if global_type != value_type { if global_type != value_type {
return Err(Error(format!("Trying to update global {} of type {:?} with value of type {:?}", index, global_type, value_type))); return Err(Error(format!("Trying to update global {} of type {:?} with value of type {:?}", index, global_type, value_type)));
} }
@ -610,47 +608,50 @@ impl<'a> FunctionValidationContext<'a> {
Ok(self.value_stack.push(value_type.into())?) Ok(self.value_stack.push(value_type.into())?)
} }
fn pop_value(&mut self, value_type: StackValueType) -> Result<(), Error> { fn pop_value(&mut self, value_type: StackValueType) -> Result<StackValueType, Error> {
self.check_stack_access()?; let (is_stack_polymorphic, label_value_stack_len) = {
match self.value_stack.pop()? { let frame = self.top_label()?;
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()), (frame.polymorphic_stack, frame.value_stack_len)
StackValueType::Any => Ok(()), };
StackValueType::AnyUnlimited => { let stack_is_empty = self.value_stack.len() == label_value_stack_len;
self.value_stack.push(StackValueType::AnyUnlimited)?; let actual_value = if stack_is_empty && is_stack_polymorphic {
Ok(()) StackValueType::Any
}, } else {
stack_value_type @ _ => Err(Error(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))), self.check_stack_access()?;
self.value_stack.pop()?
};
match actual_value {
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => {
Ok(actual_value)
}
StackValueType::Any => Ok(actual_value),
stack_value_type @ _ => Err(Error(format!(
"Expected value of type {:?} on top of stack. Got {:?}",
value_type, stack_value_type
))),
} }
} }
fn tee_value(&mut self, value_type: StackValueType) -> Result<(), Error> { fn check_stack_access(&self) -> Result<(), Error> {
self.check_stack_access()?; let value_stack_min = self.frame_stack.top().expect("at least 1 topmost block").value_stack_len;
match *self.value_stack.top()? { if self.value_stack.len() > value_stack_min {
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()), Ok(())
StackValueType::Any | StackValueType::AnyUnlimited => Ok(()), } else {
stack_value_type @ _ => Err(Error(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))), Err(Error("Trying to access parent frame stack values.".into()))
} }
} }
fn pop_any_value(&mut self) -> Result<StackValueType, Error> { fn tee_value(&mut self, value_type: StackValueType) -> Result<StackValueType, Error> {
self.check_stack_access()?; let value = self.pop_value(value_type)?;
match self.value_stack.pop()? { self.push_value(value)?;
StackValueType::Specific(stack_value_type) => Ok(StackValueType::Specific(stack_value_type)), Ok(value)
StackValueType::Any => Ok(StackValueType::Any),
StackValueType::AnyUnlimited => {
self.value_stack.push(StackValueType::AnyUnlimited)?;
Ok(StackValueType::Any)
},
}
}
fn tee_any_value(&mut self) -> Result<StackValueType, Error> {
self.check_stack_access()?;
Ok(self.value_stack.top().map(Clone::clone)?)
} }
fn unreachable(&mut self) -> Result<(), Error> { fn unreachable(&mut self) -> Result<(), Error> {
Ok(self.value_stack.push(StackValueType::AnyUnlimited)?) let frame = self.frame_stack.top_mut()?;
self.value_stack.resize(frame.value_stack_len, StackValueType::Any);
frame.polymorphic_stack = true;
Ok(())
} }
fn top_label(&self) -> Result<&BlockFrame, Error> { fn top_label(&self) -> Result<&BlockFrame, Error> {
@ -665,23 +666,31 @@ impl<'a> FunctionValidationContext<'a> {
branch_position: self.position, branch_position: self.position,
end_position: self.position, end_position: self.position,
value_stack_len: self.value_stack.len(), value_stack_len: self.value_stack.len(),
polymorphic_stack: false,
})?) })?)
} }
fn pop_label(&mut self) -> Result<InstructionOutcome, Error> { fn pop_label(&mut self) -> Result<InstructionOutcome, Error> {
let frame = self.frame_stack.pop()?; // Don't pop frame yet. This is essential since we still might pop values from the value stack
let actual_value_type = if self.value_stack.len() > frame.value_stack_len { // and this in turn requires current frame to check whether or not we've reached
Some(self.value_stack.pop()?) // unreachable.
} else { let block_type = self.frame_stack.top()?.block_type;
None match block_type {
}; BlockType::NoResult => (),
self.value_stack.resize(frame.value_stack_len, StackValueType::Any); BlockType::Value(required_value_type) => {
self.pop_value(StackValueType::Specific(required_value_type))?;
match frame.block_type { }
BlockType::NoResult if actual_value_type.map(|vt| vt.is_any_unlimited()).unwrap_or(true) => (),
BlockType::Value(required_value_type) if actual_value_type.map(|vt| vt == required_value_type).unwrap_or(false) => (),
_ => return Err(Error(format!("Expected block to return {:?} while it has returned {:?}", frame.block_type, actual_value_type))),
} }
let frame = self.frame_stack.pop()?;
if self.value_stack.len() != frame.value_stack_len {
return Err(Error(format!(
"Unexpected stack height {}, expected {}",
self.value_stack.len(),
frame.value_stack_len
)));
}
if !self.frame_stack.is_empty() { if !self.frame_stack.is_empty() {
self.labels.insert(frame.begin_position, self.position); self.labels.insert(frame.begin_position, self.position);
} }
@ -707,15 +716,6 @@ impl<'a> FunctionValidationContext<'a> {
.ok_or(Error(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len()))) .ok_or(Error(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len())))
} }
fn check_stack_access(&self) -> Result<(), Error> {
let value_stack_min = self.frame_stack.top().expect("at least 1 topmost block").value_stack_len;
if self.value_stack.len() > value_stack_min {
Ok(())
} else {
Err(Error("Trying to access parent frame stack values.".into()))
}
}
fn into_labels(self) -> HashMap<usize, usize> { fn into_labels(self) -> HashMap<usize, usize> {
self.labels self.labels
} }
@ -729,16 +729,9 @@ impl StackValueType {
} }
} }
fn is_any_unlimited(&self) -> bool {
match self {
&StackValueType::AnyUnlimited => true,
_ => false,
}
}
fn value_type(&self) -> ValueType { fn value_type(&self) -> ValueType {
match self { match self {
&StackValueType::Any | &StackValueType::AnyUnlimited => unreachable!("must be checked by caller"), &StackValueType::Any => unreachable!("must be checked by caller"),
&StackValueType::Specific(value_type) => value_type, &StackValueType::Specific(value_type) => value_type,
} }
} }
@ -752,7 +745,7 @@ impl From<ValueType> for StackValueType {
impl PartialEq<StackValueType> for StackValueType { impl PartialEq<StackValueType> for StackValueType {
fn eq(&self, other: &StackValueType) -> bool { fn eq(&self, other: &StackValueType) -> bool {
if self.is_any() || other.is_any() || self.is_any_unlimited() || other.is_any_unlimited() { if self.is_any() || other.is_any() {
true true
} else { } else {
self.value_type() == other.value_type() self.value_type() == other.value_type()
@ -762,7 +755,7 @@ impl PartialEq<StackValueType> for StackValueType {
impl PartialEq<ValueType> for StackValueType { impl PartialEq<ValueType> for StackValueType {
fn eq(&self, other: &ValueType) -> bool { fn eq(&self, other: &ValueType) -> bool {
if self.is_any() || self.is_any_unlimited() { if self.is_any() {
true true
} else { } else {
self.value_type() == *other self.value_type() == *other