Path: blob/main/cranelift/interpreter/src/frame.rs
1692 views
//! Implements a call frame (activation record) for the Cranelift interpreter.12use cranelift_codegen::data_value::DataValue;3use cranelift_codegen::ir::{Function, Value as ValueRef, types};4use cranelift_entity::EntityRef;5use log::trace;67/// The type used for ensuring [Frame](crate::frame::Frame) entries conform to the expected memory layout.8pub(crate) type Entries = Vec<Option<DataValue>>;910/// Holds the mutable elements of an interpreted function call.11#[derive(Debug)]12pub struct Frame<'a> {13/// The currently executing function.14function: &'a Function,15/// The current mapping of SSA value-references to their actual values. For efficiency, each SSA value is used as an16/// index into the Vec, meaning some slots may be unused.17registers: Entries,18}1920impl<'a> Frame<'a> {21/// Construct a new [Frame] for a function. This allocates a slot in the hash map for each SSA `Value` (renamed to22/// `ValueRef` here) which should mean that no additional allocations are needed while interpreting the frame.23pub fn new(function: &'a Function) -> Self {24let num_slots = function.dfg.num_values();25trace!("Create new frame for function: {}", function.signature);26Self {27function,28registers: vec![None; num_slots],29}30}3132/// Retrieve the actual value associated with an SSA reference.33#[inline]34pub fn get(&self, name: ValueRef) -> &DataValue {35assert!(name.index() < self.registers.len());36trace!("Get {name}");37&self38.registers39.get(name.index())40.unwrap_or_else(|| panic!("unknown value: {name}"))41.as_ref()42.or_else(|| {43// We couldn't find the `name` value directly in `registers`, but it is still44// possible that it is aliased to another value.4546// If we are looking up an undefined value it will have an invalid type, return47// before trying to resolve it.48if self.function.dfg.value_type(name) == types::INVALID {49return None;50}5152let alias = self.function.dfg.resolve_aliases(name);53self.registers54.get(alias.index())55.unwrap_or_else(|| panic!("unknown value: {alias}"))56.as_ref()57})58.unwrap_or_else(|| panic!("empty slot: {name}"))59}6061/// Retrieve multiple SSA references; see `get`.62pub fn get_all(&self, names: &[ValueRef]) -> Vec<DataValue> {63names.iter().map(|r| self.get(*r)).cloned().collect()64}6566/// Assign `value` to the SSA reference `name`.67#[inline]68pub fn set(&mut self, name: ValueRef, value: DataValue) -> Option<DataValue> {69assert!(name.index() < self.registers.len());70trace!("Set {name} -> {value}");71std::mem::replace(&mut self.registers[name.index()], Some(value))72}7374/// Assign to multiple SSA references; see `set`.75pub fn set_all(&mut self, names: &[ValueRef], values: Vec<DataValue>) {76assert_eq!(names.len(), values.len());77for (n, v) in names.iter().zip(values) {78self.set(*n, v);79}80}8182/// Rename all of the SSA references in `old_names` to those in `new_names`. This will remove83/// any old references that are not in `old_names`. TODO This performs an extra allocation that84/// could be removed if we copied the values in the right order (i.e. when modifying in place,85/// we need to avoid changing a value before it is referenced).86pub fn rename(&mut self, old_names: &[ValueRef], new_names: &[ValueRef]) {87trace!("Renaming {old_names:?} -> {new_names:?}");88assert_eq!(old_names.len(), new_names.len());89let new_registers = vec![None; self.registers.len()];90let mut old_registers = std::mem::replace(&mut self.registers, new_registers);91self.registers = vec![None; self.registers.len()];92for (&on, &nn) in old_names.iter().zip(new_names) {93let value = std::mem::replace(&mut old_registers[on.index()], None);94self.registers[nn.index()] = value;95}96}9798/// Accessor for the current entries in the frame.99pub fn entries_mut(&mut self) -> &mut [Option<DataValue>] {100&mut self.registers101}102103/// Accessor for the [`Function`] of this frame.104pub fn function(&self) -> &'a Function {105self.function106}107}108109#[cfg(test)]110mod tests {111use super::*;112use cranelift_codegen::ir::InstBuilder;113use cranelift_codegen::ir::immediates::{Ieee32, Ieee64};114use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};115use cranelift_reader::parse_functions;116117/// Helper to create a function from CLIF IR.118fn function(code: &str) -> Function {119parse_functions(code).unwrap().into_iter().next().unwrap()120}121122/// Build an empty function with a single return.123fn empty_function() -> Function {124let mut func = Function::new();125let mut context = FunctionBuilderContext::new();126let mut builder = FunctionBuilder::new(&mut func, &mut context);127let block = builder.create_block();128builder.switch_to_block(block);129builder.ins().return_(&[]);130func131}132133#[test]134fn construction() {135let func = empty_function();136// Construction should not fail.137Frame::new(&func);138}139140#[test]141fn assignment_and_retrieval() {142let func = function("function %test(i32) -> i32 { block0(v0:i32): return v0 }");143let mut frame = Frame::new(&func);144let ssa_value_ref = ValueRef::from_u32(0);145let fortytwo = DataValue::I32(42);146147// Verify that setting a valid SSA ref will make the value retrievable.148frame.set(ssa_value_ref, fortytwo.clone());149assert_eq!(frame.get(ssa_value_ref), &fortytwo);150}151152#[test]153fn assignment_to_extra_slots() {154let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");155let mut frame = Frame::new(&func);156let ssa_value_ref = ValueRef::from_u32(5);157let fortytwo = DataValue::I32(42);158159// Due to how Cranelift organizes its SSA values, the use of v10 defines 11 slots for values160// to fit in--the following should work.161frame.set(ssa_value_ref, fortytwo.clone());162assert_eq!(frame.get(ssa_value_ref), &fortytwo);163}164165#[test]166#[should_panic(expected = "assertion failed: name.index() < self.registers.len()")]167fn invalid_assignment() {168let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");169let mut frame = Frame::new(&func);170let fortytwo = DataValue::I32(42);171172// Since the SSA value ref points to 42 and the function only has 11 slots, this should173// fail. TODO currently this is a panic under the assumption we will not set indexes outside174// of the valid SSA value range but it might be better as a result.175frame.set(ValueRef::from_u32(11), fortytwo.clone());176}177178#[test]179#[should_panic(expected = "assertion failed: name.index() < self.registers.len()")]180fn retrieve_nonexistent_value() {181let func = empty_function();182let frame = Frame::new(&func);183let ssa_value_ref = ValueRef::from_u32(1);184185// Retrieving a non-existent value should return an error.186frame.get(ssa_value_ref);187}188189#[test]190#[should_panic(expected = "empty slot: v5")]191fn retrieve_and_assign_multiple_values() {192let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");193let mut frame = Frame::new(&func);194let ssa_value_refs = [195ValueRef::from_u32(2),196ValueRef::from_u32(4),197ValueRef::from_u32(6),198];199let values = vec![200DataValue::I8(1),201DataValue::I8(42),202DataValue::F32(Ieee32::from(0.42)),203];204205// We can assign and retrieve multiple (cloned) values.206frame.set_all(&ssa_value_refs, values.clone());207let retrieved_values = frame.get_all(&ssa_value_refs);208assert_eq!(values, retrieved_values);209210// But if we attempt to retrieve an invalid value we should get an error:211frame.get_all(&[ValueRef::from_u32(2), ValueRef::from_u32(5)]);212}213214#[test]215#[should_panic(expected = "empty slot: v10")]216fn rename() {217let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");218let mut frame = Frame::new(&func);219let old_ssa_value_refs = [ValueRef::from_u32(9), ValueRef::from_u32(10)];220let values = vec![DataValue::I8(1), DataValue::F64(Ieee64::from(0.0))];221frame.set_all(&old_ssa_value_refs, values.clone());222223// Rename the old SSA values to the new values.224let new_ssa_value_refs = [ValueRef::from_u32(4), ValueRef::from_u32(2)];225frame.rename(&old_ssa_value_refs, &new_ssa_value_refs);226227// Now we should be able to retrieve new values and the old ones should fail.228assert_eq!(frame.get_all(&new_ssa_value_refs), values);229frame.get(ValueRef::from_u32(10));230}231232#[test]233#[should_panic(expected = "empty slot: v2")]234fn rename_duplicates_causes_inconsistency() {235let func = function("function %test(i32) -> i32 { block0(v10:i32): return v10 }");236let mut frame = Frame::new(&func);237let old_ssa_value_refs = [ValueRef::from_u32(1), ValueRef::from_u32(9)];238let values = vec![DataValue::I8(1), DataValue::F64(Ieee64::from(f64::NAN))];239frame.set_all(&old_ssa_value_refs, values.clone());240241// Rename the old SSA values to the new values.242let old_duplicated_ssa_value_refs = [ValueRef::from_u32(1), ValueRef::from_u32(1)];243let new_ssa_value_refs = [ValueRef::from_u32(4), ValueRef::from_u32(2)];244frame.rename(&old_duplicated_ssa_value_refs, &new_ssa_value_refs);245246// If we use duplicates then subsequent renamings (v1 -> v2) will be empty.247frame.get(ValueRef::from_u32(2));248}249}250251252