diff --git a/src/protocol/eval/executor.rs b/src/protocol/eval/executor.rs index 00e18b174b087d514ebe4b29e8760a104de3c0bc..950826a2f2cf5d4412098f768ba442675b0b27d7 100644 --- a/src/protocol/eval/executor.rs +++ b/src/protocol/eval/executor.rs @@ -26,8 +26,9 @@ pub(crate) enum ExprInstruction { #[derive(Debug, Clone)] pub(crate) struct Frame { - pub(crate) definition: DefinitionId, - pub(crate) monomorph_idx: i32, + pub(crate) definition: ProcedureDefinitionId, + pub(crate) monomorph_type_id: TypeId, + pub(crate) monomorph_index: usize, pub(crate) position: StatementId, pub(crate) expr_stack: VecDeque, // hack for expression evaluation, evaluated by popping from back pub(crate) expr_values: VecDeque, // hack for expression results, evaluated by popping from front/back @@ -36,37 +37,34 @@ pub(crate) struct Frame { impl Frame { /// Creates a new execution frame. Does not modify the stack in any way. - pub fn new(heap: &Heap, definition_id: DefinitionId, monomorph_idx: i32) -> Self { + pub fn new(heap: &Heap, definition_id: ProcedureDefinitionId, monomorph_type_id: TypeId, monomorph_index: u32) -> Self { let definition = &heap[definition_id]; - let first_statement = match definition { - Definition::Component(definition) => definition.body, - Definition::Function(definition) => definition.body, - _ => unreachable!("initializing frame with {:?} instead of a function/component", definition), - }; + let outer_scope_id = definition.scope; + let first_statement_id = definition.body; // Another not-so-pretty thing that has to be replaced somewhere in the // future... - fn determine_max_stack_size(heap: &Heap, block_id: BlockStatementId, max_size: &mut u32) { - let block_stmt = &heap[block_id]; - debug_assert!(block_stmt.next_unique_id_in_scope >= 0); + fn determine_max_stack_size(heap: &Heap, scope_id: ScopeId, max_size: &mut u32) { + let scope = &heap[scope_id]; // Check current block - let cur_size = block_stmt.next_unique_id_in_scope as u32; + let cur_size = scope.next_unique_id_in_scope as u32; if cur_size > *max_size { *max_size = cur_size; } // And child blocks - for child_scope in &block_stmt.scope_node.nested { - determine_max_stack_size(heap, child_scope.to_block(), max_size); + for child_scope in &scope.nested { + determine_max_stack_size(heap, *child_scope, max_size); } } let mut max_stack_size = 0; - determine_max_stack_size(heap, first_statement, &mut max_stack_size); + determine_max_stack_size(heap, outer_scope_id, &mut max_stack_size); Frame{ definition: definition_id, - monomorph_idx, - position: first_statement.upcast(), + monomorph_type_id, + monomorph_index: monomorph_index as usize, + position: first_statement_id.upcast(), expr_stack: VecDeque::with_capacity(128), expr_values: VecDeque::with_capacity(128), max_stack_size, @@ -209,10 +207,13 @@ pub enum EvalContinuation { BlockFires(PortId), BlockGet(PortId), Put(PortId, ValueGroup), + SelectStart(u32, u32), // (num_cases, num_ports_total) + SelectRegisterPort(u32, u32, PortId), // (case_index, port_index_in_case, port_id) + SelectWait, // wait until select can continue // Returned only in non-sync mode ComponentTerminated, SyncBlockStart, - NewComponent(DefinitionId, i32, ValueGroup), + NewComponent(ProcedureDefinitionId, TypeId, ValueGroup), NewChannel, } @@ -225,14 +226,15 @@ pub struct Prompt { } impl Prompt { - pub fn new(_types: &TypeTable, heap: &Heap, def: DefinitionId, monomorph_idx: i32, args: ValueGroup) -> Self { + pub fn new(types: &TypeTable, heap: &Heap, def: ProcedureDefinitionId, type_id: TypeId, args: ValueGroup) -> Self { let mut prompt = Self{ frames: Vec::new(), store: Store::new(), }; // Maybe do typechecking in the future? - let new_frame = Frame::new(heap, def, monomorph_idx); + let monomorph_index = types.get_monomorph(type_id).variant.as_procedure().monomorph_index; + let new_frame = Frame::new(heap, def, type_id, monomorph_index); let max_stack_size = new_frame.max_stack_size; prompt.frames.push(new_frame); args.into_store(&mut prompt.store); @@ -292,13 +294,13 @@ impl Prompt { // Checking if we're at the end of execution let cur_frame = self.frames.last_mut().unwrap(); if cur_frame.position.is_invalid() { - if heap[cur_frame.definition].is_function() { + if heap[cur_frame.definition].kind == ProcedureKind::Function { todo!("End of function without return, return an evaluation error"); } return Ok(EvalContinuation::ComponentTerminated); } - debug_log!("Taking step in '{}'", heap[cur_frame.definition].identifier().value.as_str()); + debug_log!("Taking step in '{}'", heap[cur_frame.definition].identifier.value.as_str()); // Execute all pending expressions while !cur_frame.expr_stack.is_empty() { @@ -480,8 +482,8 @@ impl Prompt { }, Expression::Select(expr) => { let subject= cur_frame.expr_values.pop_back().unwrap(); - let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx); - let field_idx = mono_data.expr_data[expr.unique_id_in_definition as usize].field_or_monomorph_idx as u32; + let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index]; + let field_idx = mono_data.expr_info[expr.type_index as usize].variant.as_select() as u32; // Note: same as above: clone if value lives on expr stack, simply // refer to it if it already lives on the stack/heap. @@ -528,8 +530,9 @@ impl Prompt { } Literal::Integer(lit_value) => { use ConcreteTypePart as CTP; - let def_types = types.get_procedure_monomorph(cur_frame.monomorph_idx); - let concrete_type = &def_types.expr_data[expr.unique_id_in_definition as usize].expr_type; + let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index]; + let type_id = mono_data.expr_info[expr.type_index as usize].type_id; + let concrete_type = &types.get_monomorph(type_id).concrete_type; debug_assert_eq!(concrete_type.parts.len(), 1); match concrete_type.parts[0] { @@ -576,14 +579,15 @@ impl Prompt { cur_frame.expr_values.push_back(value); }, Expression::Cast(expr) => { - let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx); - let output_type = &mono_data.expr_data[expr.unique_id_in_definition as usize].expr_type; + let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index]; + let type_id = mono_data.expr_info[expr.type_index as usize].type_id; + let concrete_type = &types.get_monomorph(type_id).concrete_type; // Typechecking reduced this to two cases: either we // have casting noop (same types), or we're casting // between integer/bool/char types. let subject = cur_frame.expr_values.pop_back().unwrap(); - match apply_casting(&mut self.store, output_type, &subject) { + match apply_casting(&mut self.store, concrete_type, &subject) { Ok(value) => cur_frame.expr_values.push_back(value), Err(msg) => { return Err(EvalError::new_error_at_expr(self, modules, heap, expr.this.upcast(), msg)); @@ -651,12 +655,7 @@ impl Prompt { Method::Fires => { let port_value = cur_frame.expr_values.pop_front().unwrap(); let port_value_deref = self.store.maybe_read_ref(&port_value).clone(); - - let port_id = match port_value_deref { - Value::Input(port_id) => port_id, - Value::Output(port_id) => port_id, - _ => unreachable!("executor calling 'fires' on value {:?}", port_value_deref), - }; + let port_id = port_value_deref.as_port_id(); match ctx.fires(port_id) { None => { @@ -734,10 +733,34 @@ impl Prompt { self.store.drop_heap_pos(value_heap_pos); println!("{}", message); }, + Method::SelectStart => { + let num_cases = self.store.maybe_read_ref(&cur_frame.expr_values.pop_front().unwrap()).as_uint32(); + let num_ports = self.store.maybe_read_ref(&cur_frame.expr_values.pop_front().unwrap()).as_uint32(); + + return Ok(EvalContinuation::SelectStart(num_cases, num_ports)); + }, + Method::SelectRegisterCasePort => { + let case_index = self.store.maybe_read_ref(&cur_frame.expr_values.pop_front().unwrap()).as_uint32(); + let port_index = self.store.maybe_read_ref(&cur_frame.expr_values.pop_front().unwrap()).as_uint32(); + let port_value = self.store.maybe_read_ref(&cur_frame.expr_values.pop_front().unwrap()).as_port_id(); + + return Ok(EvalContinuation::SelectRegisterPort(case_index, port_index, port_value)); + }, + Method::SelectWait => { + match ctx.performed_select_wait() { + Some(select_index) => { + cur_frame.expr_values.push_back(Value::UInt32(select_index)); + }, + None => { + cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr.this.upcast())); + return Ok(EvalContinuation::SelectWait) + }, + } + }, Method::UserComponent => { // This is actually handled by the evaluation // of the statement. - debug_assert_eq!(heap[expr.definition].parameters().len(), cur_frame.expr_values.len()); + debug_assert_eq!(heap[expr.procedure].parameters.len(), cur_frame.expr_values.len()); debug_assert_eq!(heap[cur_frame.position].as_new().expression, expr.this) }, Method::UserFunction => { @@ -758,11 +781,11 @@ impl Prompt { } // Determine the monomorph index of the function we're calling - let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx); - let call_data = &mono_data.expr_data[expr.unique_id_in_definition as usize]; + let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index]; + let (type_id, monomorph_index) = mono_data.expr_info[expr.type_index as usize].variant.as_procedure(); // Push the new frame and reserve its stack size - let new_frame = Frame::new(heap, expr.definition, call_data.field_or_monomorph_idx); + let new_frame = Frame::new(heap, expr.procedure, type_id, monomorph_index); let new_stack_size = new_frame.max_stack_size; self.frames.push(new_frame); self.store.cur_stack_boundary = new_stack_boundary; @@ -771,7 +794,7 @@ impl Prompt { // To simplify the logic a little bit we will now // return and ask our caller to call us again return Ok(EvalContinuation::Stepping); - }, + } } }, Expression::Variable(expr) => { @@ -817,7 +840,8 @@ impl Prompt { }, Statement::EndBlock(stmt) => { let block = &heap[stmt.start_block]; - self.store.clear_stack(block.first_unique_id_in_scope as usize); + let scope = &heap[block.scope]; + self.store.clear_stack(scope.first_unique_id_in_scope as usize); cur_frame.position = stmt.next; Ok(EvalContinuation::Stepping) @@ -825,13 +849,13 @@ impl Prompt { Statement::Local(stmt) => { match stmt { LocalStatement::Memory(stmt) => { - if cfg!(debug_assertions) { + dbg_code!({ let variable = &heap[stmt.variable]; debug_assert!(match self.store.read_ref(ValueId::Stack(variable.unique_id_in_scope as u32)) { Value::Unassigned => false, _ => true, }); - } + }); cur_frame.position = stmt.next; Ok(EvalContinuation::Stepping) @@ -842,7 +866,7 @@ impl Prompt { match ctx.created_channel() { None => { // No channel is pending. So request one - Ok(EvalContinuation::NewChannel) + Ok(EvalContinuation::NewChannel) }, Some((put_port, get_port)) => { self.store.write(ValueId::Stack(heap[stmt.from].unique_id_in_scope as u32), put_port); @@ -864,9 +888,9 @@ impl Prompt { let test_value = cur_frame.expr_values.pop_back().unwrap(); let test_value = self.store.maybe_read_ref(&test_value).as_bool(); if test_value { - cur_frame.position = stmt.true_body.upcast(); - } else if let Some(false_body) = stmt.false_body { - cur_frame.position = false_body.upcast(); + cur_frame.position = stmt.true_case.body; + } else if let Some(false_body) = stmt.false_case { + cur_frame.position = false_body.body; } else { // Not true, and no false body cur_frame.position = stmt.end_if.upcast(); @@ -876,6 +900,13 @@ impl Prompt { }, Statement::EndIf(stmt) => { cur_frame.position = stmt.next; + let if_stmt = &heap[stmt.start_if]; + debug_assert_eq!( + heap[if_stmt.true_case.scope].first_unique_id_in_scope, + heap[if_stmt.false_case.unwrap_or(if_stmt.true_case).scope].first_unique_id_in_scope, + ); + let scope = &heap[if_stmt.true_case.scope]; + self.store.clear_stack(scope.first_unique_id_in_scope as usize); Ok(EvalContinuation::Stepping) }, Statement::While(stmt) => { @@ -883,7 +914,7 @@ impl Prompt { let test_value = cur_frame.expr_values.pop_back().unwrap(); let test_value = self.store.maybe_read_ref(&test_value).as_bool(); if test_value { - cur_frame.position = stmt.body.upcast(); + cur_frame.position = stmt.body; } else { cur_frame.position = stmt.end_while.upcast(); } @@ -892,7 +923,9 @@ impl Prompt { }, Statement::EndWhile(stmt) => { cur_frame.position = stmt.next; - + let start_while = &heap[stmt.start_while]; + let scope = &heap[start_while.scope]; + self.store.clear_stack(scope.first_unique_id_in_scope as usize); Ok(EvalContinuation::Stepping) }, Statement::Break(stmt) => { @@ -906,27 +939,30 @@ impl Prompt { Ok(EvalContinuation::Stepping) }, Statement::Synchronous(stmt) => { - cur_frame.position = stmt.body.upcast(); + cur_frame.position = stmt.body; Ok(EvalContinuation::SyncBlockStart) }, Statement::EndSynchronous(stmt) => { cur_frame.position = stmt.next; + let start_synchronous = &heap[stmt.start_sync]; + let scope = &heap[start_synchronous.scope]; + self.store.clear_stack(scope.first_unique_id_in_scope as usize); Ok(EvalContinuation::SyncBlockEnd) }, Statement::Fork(stmt) => { if stmt.right_body.is_none() { // No reason to fork - cur_frame.position = stmt.left_body.upcast(); + cur_frame.position = stmt.left_body; } else { // Need to fork if let Some(go_left) = ctx.performed_fork() { // Runtime has created a fork if go_left { - cur_frame.position = stmt.left_body.upcast(); + cur_frame.position = stmt.left_body; } else { - cur_frame.position = stmt.right_body.unwrap().upcast(); + cur_frame.position = stmt.right_body.unwrap(); } } else { // Request the runtime to create a fork of the current @@ -942,16 +978,24 @@ impl Prompt { Ok(EvalContinuation::Stepping) }, - Statement::Select(_stmt) => { - todo!("implement select evaluation") + Statement::Select(stmt) => { + // This is a trampoline for the statements that were placed by + // the AST transformation pass + cur_frame.position = stmt.next; + + Ok(EvalContinuation::Stepping) }, Statement::EndSelect(stmt) => { cur_frame.position = stmt.next; + let start_select = &heap[stmt.start_select]; + if let Some(select_case) = start_select.cases.first() { + let scope = &heap[select_case.scope]; + self.store.clear_stack(scope.first_unique_id_in_scope as usize); + } Ok(EvalContinuation::Stepping) }, Statement::Return(_stmt) => { - debug_assert!(heap[cur_frame.definition].is_function()); debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for return statement"); // The preceding frame has executed a call, so is expecting the @@ -998,14 +1042,13 @@ impl Prompt { }, Statement::New(stmt) => { let call_expr = &heap[stmt.expression]; - debug_assert!(heap[call_expr.definition].is_component()); debug_assert_eq!( - cur_frame.expr_values.len(), heap[call_expr.definition].parameters().len(), + cur_frame.expr_values.len(), heap[call_expr.procedure].parameters.len(), "mismatch in expr stack size and number of arguments for new statement" ); - let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx); - let expr_data = &mono_data.expr_data[call_expr.unique_id_in_definition as usize]; + let mono_data = &heap[cur_frame.definition].monomorphs[cur_frame.monomorph_index]; + let type_id = mono_data.expr_info[call_expr.type_index as usize].variant.as_procedure().0; // Note that due to expression value evaluation they exist in // reverse order on the stack. @@ -1017,7 +1060,6 @@ impl Prompt { // Construct argument group, thereby copying heap regions let argument_group = ValueGroup::from_store(&self.store, &args); - // println!("Creating {} with\n{:#?}", heap[call_expr.definition].identifier().value.as_str(), argument_group); // Clear any heap regions for arg in &args { @@ -1026,7 +1068,7 @@ impl Prompt { cur_frame.position = stmt.next; - Ok(EvalContinuation::NewComponent(call_expr.definition, expr_data.field_or_monomorph_idx, argument_group)) + Ok(EvalContinuation::NewComponent(call_expr.procedure, type_id, argument_group)) }, Statement::Expression(stmt) => { // The expression has just been completely evaluated. Some