From 0776fca548ca3b2bdb9642b21f38c6d8d9123265 2022-02-04 14:07:10 From: MH Date: 2022-02-04 14:07:10 Subject: [PATCH] WIP: Rewriting select guards by inserting temp variables --- diff --git a/src/collections/string_pool.rs b/src/collections/string_pool.rs index 5a31cb902ff697833f222b25997d0e90574810b4..80ced6346d462aef57339049fc57f06395c1e473 100644 --- a/src/collections/string_pool.rs +++ b/src/collections/string_pool.rs @@ -1,4 +1,4 @@ -use std::ptr::null_mut; +use std::ptr::{null_mut, null}; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; @@ -29,6 +29,12 @@ impl<'a> StringRef<'a> { StringRef{ data, length, _phantom: PhantomData } } + /// `new_empty` creates a empty StringRef. It is a null pointer with a + /// length of zero. + pub(crate) fn new_empty() -> StringRef<'static> { + StringRef{ data: null(), length: 0, _phantom: PhantomData } + } + pub fn as_str(&self) -> &'a str { unsafe { let slice = std::slice::from_raw_parts::<'a, u8>(self.data, self.length); @@ -161,6 +167,13 @@ unsafe impl Send for StringPool {} mod tests { use super::*; + #[test] + fn display_empty_string_ref() { + // Makes sure that null pointer inside StringRef will not cause issues + let v = StringRef::new_empty(); + let val = format!("{}{:?}", v, v); + } + #[test] fn test_string_just_fits() { let large = "0".repeat(SLAB_SIZE); diff --git a/src/protocol/ast.rs b/src/protocol/ast.rs index 1a8b15815835a0aa5b320791193ad9cfa98942d1..ca6efe6af2637d61f1d1d50fa3cd87285916d4d0 100644 --- a/src/protocol/ast.rs +++ b/src/protocol/ast.rs @@ -340,6 +340,15 @@ pub struct Identifier { pub value: StringRef<'static>, } +impl Identifier { + pub(crate) fn new_empty(span: InputSpan) -> Identifier { + return Identifier{ + span, + value: StringRef::new_empty(), + }; + } +} + impl PartialEq for Identifier { fn eq(&self, other: &Self) -> bool { return self.value == other.value @@ -1432,7 +1441,7 @@ pub enum ExpressionParent { Return(ReturnStatementId), New(NewStatementId), ExpressionStmt(ExpressionStatementId), - Expression(ExpressionId, u32) // index within expression (e.g LHS or RHS of expression) + Expression(ExpressionId, u32) // index within expression (e.g LHS or RHS of expression, or index in array literal, etc.) } impl ExpressionParent { @@ -1530,6 +1539,23 @@ impl Expression { } } + pub fn parent_mut(&mut self) -> &mut ExpressionParent { + match self { + Expression::Assignment(expr) => &mut expr.parent, + Expression::Binding(expr) => &mut expr.parent, + Expression::Conditional(expr) => &mut expr.parent, + Expression::Binary(expr) => &mut expr.parent, + Expression::Unary(expr) => &mut expr.parent, + Expression::Indexing(expr) => &mut expr.parent, + Expression::Slicing(expr) => &mut expr.parent, + Expression::Select(expr) => &mut expr.parent, + Expression::Literal(expr) => &mut expr.parent, + Expression::Cast(expr) => &mut expr.parent, + Expression::Call(expr) => &mut expr.parent, + Expression::Variable(expr) => &mut expr.parent, + } + } + pub fn parent_expr_id(&self) -> Option { if let ExpressionParent::Expression(id, _) = self.parent() { Some(*id) @@ -1875,5 +1901,5 @@ pub struct VariableExpression { pub declaration: Option, pub used_as_binding_target: bool, pub parent: ExpressionParent, - pub unique_id_in_definition: i32, + pub unique_id_in_definition: i32, // used to index into type table after all types are determined } \ No newline at end of file diff --git a/src/protocol/parser/pass_rewriting.rs b/src/protocol/parser/pass_rewriting.rs index 3e3a117ed71dae830de1af1d9180ac5d5eb72818..bbcb177ca6141f6edcb411868c742ff7a2ec7b17 100644 --- a/src/protocol/parser/pass_rewriting.rs +++ b/src/protocol/parser/pass_rewriting.rs @@ -4,13 +4,19 @@ use crate::protocol::*; use super::visitor::*; pub(crate) struct PassRewriting { + current_scope: BlockStatementId, statement_buffer: ScopedBuffer, + call_expr_buffer: ScopedBuffer, + expression_buffer: ScopedBuffer, } impl PassRewriting { pub(crate) fn new() -> Self { Self{ + current_scope: BlockStatementId::new_invalid(), statement_buffer: ScopedBuffer::with_capacity(16), + call_expr_buffer: ScopedBuffer::with_capacity(16), + expression_buffer: ScopedBuffer::with_capacity(16), } } } @@ -36,6 +42,7 @@ impl Visitor for PassRewriting { let block_stmt = &ctx.heap[id]; let stmt_section = self.statement_buffer.start_section_initialized(&block_stmt.statements); + self.current_scope = id; for stmt_idx in 0..stmt_section.len() { self.visit_stmt(ctx, stmt_section[stmt_idx])?; } @@ -69,12 +76,142 @@ impl Visitor for PassRewriting { return self.visit_block_stmt(ctx, body_id); } + fn visit_synchronous_stmt(&mut self, ctx: &mut Ctx, id: SynchronousStatementId) -> VisitorResult { + let sync_stmt = &ctx.heap[id]; + let body_id = sync_stmt.body; + return self.visit_block_stmt(ctx, body_id); + } + // --- Visiting the select statement fn visit_select_stmt(&mut self, ctx: &mut Ctx, id: SelectStatementId) -> VisitorResult { - let stmt = &ctx.heap[id]; + // We're going to transform the select statement by a block statement + // containing builtin runtime-calls. And to do so we create temporary + // variables and move some other statements around. + let select_stmt = &ctx.heap[id]; + let mut total_num_cases = select_stmt.cases.len(); + let mut total_num_ports = 0; + + // Put heap IDs into temporary buffers to handle borrowing rules + let mut call_id_section = self.call_expr_buffer.start_section(); + let mut expr_id_section = self.expression_buffer.start_section(); + + for case in select_stmt.cases.iter() { + total_num_ports += case.involved_ports.len(); + for (call_id, expr_id) in case.involved_ports.iter().copied() { + call_id_section.push(call_id); + expr_id_section.push(expr_id); + } + } + + // Transform all of the call expressions by takings its argument (the + // port from which we `get`) and turning it into a temporary variable. + let mut transformed_stmts = Vec::with_capacity(total_num_ports); // TODO: Recompute this preallocated length, put assert at the end + let mut locals = Vec::with_capacity(total_num_ports); + + for port_var_idx in 0..call_id_section.len() { + let call_id = call_id_section[port_var_idx]; + let expr_id = expr_id_section[port_var_idx]; + let (replacement_variable_id, variable_stmt_id) = self.modify_call_and_create_replacement_variable(ctx, call_id, expr_id); + transformed_stmts.push(variable_stmt_id); + locals.push(replacement_variable_id); + } + call_id_section.forget(); + expr_id_section.forget(); + + // let block = ctx.heap.alloc_block_statement(|this| BlockStatement{ + // this, + // is_implicit: true, + // span: stmt.span, + // statements: vec![], + // end_block: EndBlockStatementId(), + // scope_node: ScopeNode {}, + // first_unique_id_in_scope: 0, + // next_unique_id_in_scope: 0, + // locals, + // labels: vec![], + // next: () + // }); return Ok(()) } +} + +impl PassRewriting { + fn modify_call_and_create_replacement_variable(&self, ctx: &mut Ctx, call_expr_id: CallExpressionId, port_expr_id: ExpressionId) -> (VariableId, MemoryStatementId) { + // Retrieve original expression which we're going to transplant into + // its own variable + let port_expr = &ctx.heap[port_expr_id]; + let port_expr_span = port_expr.full_span(); + let port_expr_unique_id = port_expr.get_unique_id_in_definition(); + + // Create the entries in the heap + let variable_expr_id = ctx.heap.alloc_variable_expression(|this| VariableExpression{ + this, + identifier: Identifier::new_empty(port_expr_span), + declaration: None, + used_as_binding_target: false, + parent: ExpressionParent::None, + unique_id_in_definition: port_expr_unique_id, + }); + let initial_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{ + this, + operator_span: port_expr_span, + full_span: port_expr_span, + left: variable_expr_id.upcast(), + operation: AssignmentOperator::Set, + right: port_expr_id, + parent: ExpressionParent::None, + unique_id_in_definition: -1, + }); + let variable_id = ctx.heap.alloc_variable(|this| Variable{ + this, + kind: VariableKind::Local, + parser_type: ParserType{ elements: vec![ParserTypeElement{ + element_span: port_expr_span, + variant: ParserTypeVariant::Inferred, + }], + full_span: port_expr_span + }, + identifier: Identifier::new_empty(port_expr_span), + relative_pos_in_block: 0, + unique_id_in_scope: 0, + }); + let variable_decl_stmt = ctx.heap.alloc_memory_statement(|this| MemoryStatement{ + this, + span: port_expr_span, + variable: variable_id, + initial_expr: initial_expr_id, + next: StatementId::new_invalid(), + }); + + // Modify all entries that required access other heap entries + let variable_expr = &mut ctx.heap[variable_expr_id]; + variable_expr.declaration = Some(variable_id); + variable_expr.parent = ExpressionParent::Expression(initial_expr_id.upcast(), 1); + + let initial_expr = &mut ctx.heap[initial_expr_id]; + initial_expr.parent = ExpressionParent::Memory(variable_decl_stmt); + + // Modify the parent of the expression that we just transplanted + let port_expr = &mut ctx.heap[port_expr_id]; + *port_expr.parent_mut() = ExpressionParent::Expression(initial_expr_id.upcast(), 1); + + // Modify the call expression (that should contain the port expression + // as the first argument) to point to the new variable + let call_arg_expr_id = ctx.heap.alloc_variable_expression(|this| VariableExpression{ + this, + identifier: Identifier::new_empty(port_expr_span), + declaration: Some(variable_id), + used_as_binding_target: false, + parent: ExpressionParent::Expression(call_expr_id.upcast(), 0), + unique_id_in_definition: port_expr_unique_id, + }); + let call_expr = &mut ctx.heap[call_expr_id]; + debug_assert!(call_expr.arguments.len() == 1 && call_expr.arguments[0] == port_expr_id); + call_expr.arguments[0] = call_arg_expr_id.upcast(); + + return (variable_id, variable_decl_stmt); + } } \ No newline at end of file