diff --git a/Cargo.lock b/Cargo.lock index 16b8f30cd0..8d4bd350d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1927,6 +1927,7 @@ name = "morphic_lib" version = "0.1.0" dependencies = [ "sha2", + "smallvec", "thiserror", ] diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index be99dec0af..b328a11975 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -146,7 +146,7 @@ fn proc_spec(proc: &Proc) -> Result { #[derive(Default)] struct Env { symbols: MutMap, - join_points: MutMap, + join_points: MutMap, } fn stmt_spec( @@ -246,7 +246,7 @@ fn stmt_spec( let jp_arg_type_id = builder.add_tuple_type(&type_ids)?; let (jpid, jp_argument) = - builder.declare_join_point(block, jp_arg_type_id, ret_type_id)?; + builder.declare_continuation(block, jp_arg_type_id, ret_type_id)?; let join_body_sub_block = { env.join_points.insert(*id, jpid); @@ -270,7 +270,7 @@ fn stmt_spec( let cont_value_id = stmt_spec(builder, env, cont_block, layout, continuation)?; env.join_points.remove(id); - builder.define_join_point(jpid, join_body_sub_block)?; + builder.define_continuation(jpid, join_body_sub_block)?; builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id)) } diff --git a/vendor/morphic_lib/Cargo.toml b/vendor/morphic_lib/Cargo.toml index bf93c8b39e..5406cf3647 100644 --- a/vendor/morphic_lib/Cargo.toml +++ b/vendor/morphic_lib/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" [dependencies] thiserror = "1.0.24" sha2 = "0.9.4" +smallvec = "1.6.1" diff --git a/vendor/morphic_lib/src/api.rs b/vendor/morphic_lib/src/api.rs index 604b02f896..56e97e1b8d 100644 --- a/vendor/morphic_lib/src/api.rs +++ b/vendor/morphic_lib/src/api.rs @@ -1,6 +1,13 @@ +use sha2::{digest::Digest, Sha256}; +use smallvec::SmallVec; use std::collections::{btree_map::Entry, BTreeMap, BTreeSet}; -use sha2::{digest::Digest, Sha256}; +use crate::util::blocks::Blocks; +use crate::util::id_bi_map::IdBiMap; +use crate::util::id_type::Count; +use crate::util::id_vec::IdVec; +use crate::util::op_graph::{Node, OpGraph}; +use crate::util::replace_none::replace_none; #[derive(Clone, thiserror::Error, Debug)] #[non_exhaustive] @@ -11,12 +18,12 @@ enum ErrorKind { BlockIdNotFound(BlockId), #[error("no value found for {0:?}")] ValueIdNotFound(ValueId), - #[error("no join point found for {0:?}")] - JoinPointIdNotFound(JoinPointId), - #[error("body of join point {0:?} is not defined")] - JoinPointNotDefined(JoinPointId), - #[error("body of join point {0:?} has already been defined")] - JoinPointAlreadyDefined(JoinPointId), + #[error("no continuation found for {0:?}")] + ContinuationIdNotFound(ContinuationId), + #[error("body of continuation {0:?} is not defined")] + ContinuationNotDefined(ContinuationId), + #[error("body of continuation {0:?} has already been defined")] + ContinuationAlreadyDefined(ContinuationId), #[error("block {0:?} has no parent")] BlockDetached(BlockId), #[error("block {0:?} already has a parent")] @@ -64,74 +71,75 @@ pub type Result = std::result::Result; bytes_id! { pub ModName; - ModNameBuf; + pub(crate) ModNameBuf; } bytes_id! { pub EntryPointName; - EntryPointNameBuf; + pub(crate) EntryPointNameBuf; } // Module-level identifiers (unique within a module): bytes_id! { pub TypeName; - TypeNameBuf; + pub(crate) TypeNameBuf; } bytes_id! { pub FuncName; - FuncNameBuf; + pub(crate) FuncNameBuf; } bytes_id! { pub ConstName; - ConstNameBuf; + pub(crate) ConstNameBuf; } // Local identifiers (unique within a function body): -/// A reference to an arena-allocated expression object in a function body. -#[repr(C)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ValueId(pub u32); +id_type! { + /// A reference to an arena-allocated expression object in a function body. + #[repr(C)] + pub ValueId(u32); +} -/// A reference to an arena-allocated join point in a function body. -/// -/// A join point is a point where multiple paths of execution "join" up again. In other words, a -/// block with multiple entry points. Join points can also be thought of as continuations that do -/// not escape. See [Compiling without Continuations](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/11/join-points-pldi17.pdf). -/// Join points are common in functional language IRs, but might also be useful (in the context of -/// this library) for encoding constructs such as for loops. -#[repr(C)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct JoinPointId(pub u32); +id_type! { + /// A reference to an arena-allocated continuation in a function body. + /// + /// See [`ExprContext::declare_continuation`], [`ExprContext::define_continuation`], and + /// [`ExprContext::add_jump`] for more information. + #[repr(C)] + pub ContinuationId(u32); +} -/// A reference to an arena-allocated block in a function body. -/// -/// A block is a sequence of zero or more expressions. Each expression has an associated -/// `ValueId`, which is unique across all blocks within its function. -#[repr(C)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct BlockId(pub u32); +id_type! { + /// A reference to an arena-allocated block in a function body. + /// + /// A block is a sequence of zero or more expressions. Each expression has an associated + /// `ValueId`, which is unique across all blocks within its function. + #[repr(C)] + pub BlockId(u32); +} -/// A reference to an arena-allocated type node in a type definition or function body. -#[repr(C)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct TypeId(pub u32); +id_type! { + /// A reference to an arena-allocated type node in a type definition or function body. + #[repr(C)] + pub TypeId(u32); +} bytes_id! { /// A client-provided name used as a key to look up callee specializations in the solution /// tables generated by the analysis engine. pub CalleeSpecVar; - pub(self) CalleeSpecBuf; + pub(crate) CalleeSpecBuf; } bytes_id! { /// A client-provided name used as a key to look up concrete update mode flags in the solution /// tables generated by the analysis engine. pub UpdateModeVar; - pub(self) UpdateModeBuf; + pub(crate) UpdateModeBuf; } // Input API: @@ -202,86 +210,6 @@ impl SeqGen { } } -/// A `TypeDef` defines the content type of a named type. -pub struct TypeDef; - -struct TypeBuilder { - tid_gen: SeqGen, -} - -impl Default for TypeBuilder { - fn default() -> Self { - Self::new() - } -} - -impl TypeBuilder { - fn new() -> Self { - Self { - tid_gen: SeqGen::new(), - } - } - - fn check(&self, id: TypeId) -> Result<()> { - self.tid_gen - .check_in_range(id.0) - .map_err(|()| ErrorKind::TypeIdNotFound(id).into()) - } -} - -impl TypeContext for TypeBuilder { - fn add_named_type(&mut self, _mod: ModName, _type: TypeName) -> TypeId { - TypeId(self.tid_gen.next()) - } - - fn add_tuple_type(&mut self, field_types: &[TypeId]) -> Result { - for &field_type in field_types { - self.check(field_type)?; - } - Ok(TypeId(self.tid_gen.next())) - } - - fn add_union_type(&mut self, variant_types: &[TypeId]) -> Result { - for &variant_type in variant_types { - self.check(variant_type)?; - } - Ok(TypeId(self.tid_gen.next())) - } - - fn add_heap_cell_type(&mut self) -> TypeId { - TypeId(self.tid_gen.next()) - } - - fn add_bag_type(&mut self, item_type: TypeId) -> Result { - self.check(item_type)?; - Ok(TypeId(self.tid_gen.next())) - } -} - -pub struct TypeDefBuilder { - inner: TypeBuilder, -} - -impl Default for TypeDefBuilder { - fn default() -> Self { - Self::new() - } -} - -impl TypeDefBuilder { - pub fn new() -> Self { - Self { - inner: TypeBuilder::new(), - } - } - - /// Create a `TypeDef` using the given type node as the root. - pub fn build(self, root: TypeId) -> Result { - self.inner.check(root)?; - Ok(TypeDef) - } -} - /// A block, together with a value to return from the block. /// /// The returned value must be in scope at the end of the block. @@ -296,46 +224,6 @@ pub struct BlockExpr(pub BlockId, pub ValueId); forward_trait! { /// A trait for constructing expressions in both function bodies and constant initializers. pub trait ExprContext { - /// Declare a join point in a `block`. - /// - /// Returning from the resulting join point after jumping to it will yield control to the - /// parent of the block in which the join point was defined. - /// - /// This function allows you to forward-declare a join point without specifying its body, - /// which makes it possible to define recursive and mutually recursive joint points. - /// However, you must eventually populate the body of each joint point via a call to - /// `define_join_point`. - /// - /// The returned ValueId is the ValueId of the *argument* to the join point, which is in - /// scope within the body of the join point. - fn declare_join_point( - &mut self, - block: BlockId, - arg_type: TypeId, - ret_type: TypeId, - ) -> Result<(JoinPointId, ValueId)>; - - /// Define a join point. - /// - /// Before a join point can be defined, it must be declared via a call to - /// `declare_join_point`. - fn define_join_point(&mut self, join_point: JoinPointId, body: BlockExpr) -> Result<()>; - - /// Add a jump to a join point. - /// - /// The rest of the API assumes that every code path produces a `ValueId`. Join points do - /// not fit this model neatly because jumping to a join point yields control to outside of - /// the current expression. Hence, this function returns a dummy unconstrained `ValueId`, - /// which can be used as if the jump were a regular expression producing a value of type - /// `unreachable_result_type`. - fn add_jump( - &mut self, - block: BlockId, - join_point: JoinPointId, - arg: ValueId, - unreachable_result_type: TypeId, - ) -> Result; - /// Add a block to the current function body. /// /// A block is a sequence of zero or more expressions. Each expression has an associated @@ -406,9 +294,9 @@ forward_trait! { /// /// The sub-block expression evaluates to the value returned by the sub-block. /// - /// This is useful in combination with join points, because returning from a join point - /// always yields control to the parent of the enclosing block in which the join point was - /// defined. Without sub-blocks, there would be no way to reduce the scope of a join point + /// This is useful in combination with continuations, because returning from a continuation + /// always yields control to the parent of the enclosing block in which the continuation was + /// defined. Without sub-blocks, there would be no way to reduce the scope of a continuation /// within a block. fn add_sub_block(&mut self, block: BlockId, sub_block: BlockExpr) -> Result; @@ -434,6 +322,16 @@ forward_trait! { /// A 'touch' expression returns the empty tuple type `()`. fn add_touch(&mut self, block: BlockId, heap_cell: ValueId) -> Result; + /// Add a 'recursive touch' expression to a block. + /// + /// Semantically, this is equivalent to 'touch'ing all heap cells reachable from `object`, + /// including heap cells nested inside tuples, unions, bags, and named types. The `object` + /// argument may be of any type, and does not necessarily need to contain any heap cells at + /// all (in which case this operation will be a no-op). + /// + /// A 'recursive touch' expression returns the empty tuple type `()`. + fn add_recursive_touch(&mut self, block: BlockId, object: ValueId) -> Result; + /// Add an 'update' expression to a block. /// /// An 'update' expression represents an attempt to "write" the contents of the heap cell. @@ -571,16 +469,285 @@ forward_trait! { named: TypeName, to_unwrap: ValueId, ) -> Result; + + /// Declare a continuation in a `block`. + /// + /// A continuation is like a local function which does not return to its caller. A + /// continuation always has a single argument, and a "body" block in which that argument is + /// in scope. Declaring a continuation has no effect on its own, but after a continuation + /// is declared it may be called with a ['jump'](ExprContext::add_jump) expression. + /// + /// Like calling a function, jumping to a continuation transfers control to its body. + /// However, unlike a function, a continuation does not return control to its caller. When + /// a continuation returns, instead of transferring control to the 'jump' expression that + /// called it, the continuation transfers control to the *parent of the `block` in which the + /// continuation was defined*. When a continuation returns, its return value becomes the + /// return value of the `block` in which it was defined. + /// + /// Continuations can be used to express constructs such as 'goto' statements, loops, + /// 'break' and 'continue' statements, early returns, tail calls, and ["join + /// points"](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/11/compiling-without-continuations.pdf). + /// + /// THe `declare_continuation` function allows you to forward-declare a continuation without + /// specifying its body, which makes it possible to define recursive and mutually recursive + /// continuations. However, you must eventually populate the body of each continuation via + /// a call to [`define_continuation`](ExprContext::define_continuation). + /// + /// The `ValueId` returned from `declare_continuation` is the `ValueId` of the *argument* to + /// the continuation, which is in scope within the body of the continuation. + fn declare_continuation( + &mut self, + block: BlockId, + arg_type: TypeId, + ret_type: TypeId, + ) -> Result<(ContinuationId, ValueId)>; + + /// Define the body of a continuation. + /// + /// Before a continuation can be defined, it must be declared via a call to + /// [`declare_continuation`](ExprContext::declare_continuation). + fn define_continuation( + &mut self, + continuation: ContinuationId, + body: BlockExpr + ) -> Result<()>; + + /// Add a 'jump' to a continuation. + /// + /// A 'jump' expression never returns, and any code which operates on the notional "return + /// value" of a 'jump' expression will be unreachable. However, in some cases it may still + /// be useful to treat a 'jump' expression as if it returned a value of a given type (for + /// example, in a `choice` expression in which one arm returns a value, and the other arm + /// ends in a 'jump'). For this reason, `add_jump` accepts an `unreachable_return_type` + /// argument which allows you to set the return type of the 'jump' expression to *any type + /// of your choosing*. This is analogous to the idea of a ["diverging + /// function"](https://doc.rust-lang.org/stable/rust-by-example/fn/diverging.html) in Rust, + /// whose return value may be used where a value of any type is expected. + fn add_jump( + &mut self, + block: BlockId, + continuation: ContinuationId, + arg: ValueId, + unreachable_result_type: TypeId, + ) -> Result; } impl FuncDefBuilder => .expr_builder; impl ConstDefBuilder => .expr_builder; } +#[derive(Clone, Debug)] +pub(crate) enum TypeOp { + // 0 inputs + Named { + named_mod: ModNameBuf, + named: TypeNameBuf, + }, + // Variadic inputs + Tuple, + // Variadic inputs + Union, + // 0 inputs + HeapCell, + // 1 input + Bag, +} + +/// A `TypeDef` defines the content type of a named type. +pub struct TypeDef; + +#[derive(Clone, Debug)] +struct TypeBuilder { + types: OpGraph, +} + +impl Default for TypeBuilder { + fn default() -> Self { + Self::new() + } +} + +impl TypeBuilder { + fn new() -> Self { + Self { + types: OpGraph::new(), + } + } + + fn check(&self, id: TypeId) -> Result<()> { + if id >= self.types.count() { + return Err(ErrorKind::TypeIdNotFound(id).into()); + } + Ok(()) + } + + fn add_node_checked(&mut self, op: TypeOp, inputs: &[TypeId]) -> Result { + for &input in inputs { + self.check(input)?; + } + Ok(self.types.add_node(op, inputs)) + } +} + +impl TypeContext for TypeBuilder { + fn add_named_type(&mut self, named_mod: ModName, named: TypeName) -> TypeId { + self.types.add_node( + TypeOp::Named { + named_mod: named_mod.into(), + named: named.into(), + }, + &[], + ) + } + + fn add_tuple_type(&mut self, field_types: &[TypeId]) -> Result { + self.add_node_checked(TypeOp::Tuple, field_types) + } + + fn add_union_type(&mut self, variant_types: &[TypeId]) -> Result { + self.add_node_checked(TypeOp::Union, variant_types) + } + + fn add_heap_cell_type(&mut self) -> TypeId { + self.types.add_node(TypeOp::HeapCell, &[]) + } + + fn add_bag_type(&mut self, item_type: TypeId) -> Result { + self.add_node_checked(TypeOp::Bag, &[item_type]) + } +} + +pub struct TypeDefBuilder { + inner: TypeBuilder, +} + +impl Default for TypeDefBuilder { + fn default() -> Self { + Self::new() + } +} + +impl TypeDefBuilder { + pub fn new() -> Self { + Self { + inner: TypeBuilder::new(), + } + } + + /// Create a `TypeDef` using the given type node as the root. + pub fn build(self, root: TypeId) -> Result { + self.inner.check(root)?; + Ok(TypeDef) + } +} + +id_type! { + pub(crate) UpdateModeVarId(u32); +} + +id_type! { + pub(crate) CalleeSpecVarId(u32); +} + +#[derive(Clone, Debug)] +pub(crate) enum Op { + // 0 inputs + Arg, + // 0 inputs + ContinuationArg, + // 0 inputs + // TODO: We may want to refactor the IR so that this is not an op. + DeclareContinuation { + continuation: ContinuationId, + }, + // 1 input + Jump { + continuation: ContinuationId, + unreachable_result_type: TypeId, + }, + // Variadic inputs + UnknownWith { + result_type: TypeId, + }, + // 1 input + Call { + callee_spec_var: CalleeSpecVarId, + callee_mod: ModNameBuf, + callee: FuncNameBuf, + }, + // 0 inputs + ConstRef { + const_mod: ModNameBuf, + const_: ConstNameBuf, + }, + // 0 inputs + Choice { + // Invariant: 'cases' must be nonempty (to ensure we can always infer the type of a `Choice` + // op). + cases: SmallVec<[BlockExpr; 6]>, + }, + // 0 inputs + SubBlock { + sub_block: BlockExpr, + }, + // 0 inputs + Terminate { + unreachable_result_type: TypeId, + }, + // 0 inputs + NewHeapCell, + // 1 input + Touch, + // 1 input + RecursiveTouch, + // 1 input + UpdateWriteOnly { + update_mode_var: UpdateModeVarId, + }, + // 0 inputs + EmptyBag { + item_type: TypeId, + }, + // 2 inputs: (bag, to_insert) + BagInsert, + // 1 input + BagGet, + // 1 input + // output: (new bag, item) + BagRemove, + // Variadic inputs + MakeTuple, + // 1 input + GetTupleField { + field_idx: u32, + }, + // 1 input + MakeUnion { + variant_types: SmallVec<[TypeId; 10]>, + variant_idx: u32, + }, + // 1 input + UnwrapUnion { + variant_idx: u32, + }, + // 1 input + MakeNamed { + named_mod: ModNameBuf, + named: TypeNameBuf, + }, + // 1 input + UnwrapNamed { + named_mod: ModNameBuf, + named: TypeNameBuf, + }, +} + #[derive(Clone, Copy, Debug)] -enum JoinPointState { - Declared, - Defined, +struct ContinuationInfo { + arg_type: TypeId, + ret_type: TypeId, + arg: ValueId, + body: Option, } #[derive(Clone, Copy, Debug)] @@ -589,15 +756,14 @@ enum BlockState { Attached, } +#[derive(Clone, Debug)] struct ExprBuilder { type_builder: TypeBuilder, - bid_gen: SeqGen, - vid_gen: SeqGen, - jid_gen: SeqGen, - blocks: BTreeMap, - join_points: BTreeMap, - callee_spec_vars: BTreeMap, - update_mode_vars: BTreeSet, + blocks: Blocks, + vals: OpGraph, + continuations: IdVec, + callee_spec_vars: IdBiMap, + update_mode_vars: IdBiMap, } impl Default for ExprBuilder { @@ -610,13 +776,11 @@ impl ExprBuilder { fn new() -> Self { Self { type_builder: TypeBuilder::new(), - bid_gen: SeqGen::new(), - vid_gen: SeqGen::new(), - jid_gen: SeqGen::new(), - blocks: BTreeMap::new(), - join_points: BTreeMap::new(), - callee_spec_vars: BTreeMap::new(), - update_mode_vars: BTreeSet::new(), + blocks: Blocks::new(), + vals: OpGraph::new(), + continuations: IdVec::new(), + callee_spec_vars: IdBiMap::new(), + update_mode_vars: IdBiMap::new(), } } @@ -625,38 +789,38 @@ impl ExprBuilder { } fn check_bid(&self, id: BlockId) -> Result<()> { - self.bid_gen - .check_in_range(id.0) - .map_err(|()| ErrorKind::BlockIdNotFound(id).into()) + if id >= self.blocks.block_count() { + return Err(ErrorKind::BlockIdNotFound(id).into()); + } + Ok(()) } fn check_vid(&self, id: ValueId) -> Result<()> { - self.vid_gen - .check_in_range(id.0) - .map_err(|()| ErrorKind::ValueIdNotFound(id).into()) + if id >= self.vals.count() { + return Err(ErrorKind::ValueIdNotFound(id).into()); + } + Ok(()) } - fn check_jid(&self, id: JoinPointId) -> Result<()> { - self.jid_gen - .check_in_range(id.0) - .map_err(|()| ErrorKind::JoinPointIdNotFound(id).into()) + fn check_cid(&self, id: ContinuationId) -> Result<()> { + if id >= self.continuations.count() { + return Err(ErrorKind::ContinuationIdNotFound(id).into()); + } + Ok(()) } fn finalize_with_root(&mut self, root: BlockExpr) -> Result<()> { self.attach_block(root.0)?; self.check_vid(root.1)?; - for (&join_point, state) in &self.join_points { - match state { - JoinPointState::Declared => { - return Err(ErrorKind::JoinPointNotDefined(join_point).into()); - } - JoinPointState::Defined => {} + for (continuation, info) in &self.continuations { + if info.body.is_none() { + return Err(ErrorKind::ContinuationNotDefined(continuation).into()); } } - for (&block, state) in &self.blocks { - match state { + for block_id in self.blocks.block_count().iter() { + match self.blocks.block_info(block_id) { BlockState::Detached => { - return Err(ErrorKind::BlockDetached(block).into()); + return Err(ErrorKind::BlockDetached(block_id).into()); } BlockState::Attached => {} } @@ -665,66 +829,82 @@ impl ExprBuilder { } fn add_argument(&mut self) -> ValueId { - ValueId(self.vid_gen.next()) + self.vals.add_node(Op::Arg, &[]) + } + + fn add_node_checked(&mut self, block: BlockId, op: Op, inputs: &[ValueId]) -> Result { + self.check_bid(block)?; + for &input in inputs { + self.check_vid(input)?; + } + let val_id = self.vals.add_node(op, inputs); + self.blocks.add_value(block, val_id); + Ok(val_id) } fn attach_block(&mut self, block: BlockId) -> Result<()> { - match self.blocks.insert(block, BlockState::Attached) { - Some(BlockState::Detached) => Ok(()), - Some(BlockState::Attached) => Err(ErrorKind::BlockAlreadyAttached(block).into()), - None => Err(ErrorKind::BlockIdNotFound(block).into()), + self.check_bid(block)?; + let state = self.blocks.block_info_mut(block); + match state { + BlockState::Detached => *state = BlockState::Attached, + BlockState::Attached => return Err(ErrorKind::BlockAlreadyAttached(block).into()), } + Ok(()) } } impl ExprContext for ExprBuilder { - fn declare_join_point( + fn declare_continuation( &mut self, block: BlockId, arg_type: TypeId, ret_type: TypeId, - ) -> Result<(JoinPointId, ValueId)> { - self.check_bid(block)?; + ) -> Result<(ContinuationId, ValueId)> { self.check_tid(arg_type)?; self.check_tid(ret_type)?; - let join_point = JoinPointId(self.jid_gen.next()); - self.join_points - .insert(join_point, JoinPointState::Declared); - Ok((join_point, ValueId(self.vid_gen.next()))) + let arg = self.vals.add_node(Op::ContinuationArg, &[]); + let continuation = self.continuations.push(ContinuationInfo { + arg_type, + ret_type, + arg, + body: None, + }); + self.add_node_checked(block, Op::DeclareContinuation { continuation }, &[])?; + Ok((continuation, arg)) } - fn define_join_point(&mut self, join_point: JoinPointId, body: BlockExpr) -> Result<()> { - self.check_jid(join_point)?; + fn define_continuation(&mut self, continuation: ContinuationId, body: BlockExpr) -> Result<()> { + self.check_cid(continuation)?; self.check_bid(body.0)?; self.check_vid(body.1)?; self.attach_block(body.0)?; - match self.join_points.insert(join_point, JoinPointState::Defined) { - Some(JoinPointState::Declared) => Ok(()), - Some(JoinPointState::Defined) => { - Err(ErrorKind::JoinPointAlreadyDefined(join_point).into()) - } - None => Err(ErrorKind::JoinPointIdNotFound(join_point).into()), - } + replace_none(&mut self.continuations[continuation].body, body) + .map_err(|_| ErrorKind::ContinuationAlreadyDefined(continuation))?; + Ok(()) } fn add_jump( &mut self, block: BlockId, - join_point: JoinPointId, + continuation: ContinuationId, arg: ValueId, unreachable_result_type: TypeId, ) -> Result { - self.check_bid(block)?; - self.check_jid(join_point)?; - self.check_vid(arg)?; + self.check_cid(continuation)?; self.check_tid(unreachable_result_type)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked( + block, + Op::Jump { + continuation, + unreachable_result_type, + }, + &[arg], + ) } fn add_block(&mut self) -> BlockId { - let block = BlockId(self.bid_gen.next()); - self.blocks.insert(block, BlockState::Detached); - block + self.blocks + .add_block(self.vals.count().0, BlockState::Detached) } fn add_unknown_with( @@ -733,12 +913,8 @@ impl ExprContext for ExprBuilder { args: &[ValueId], result_type: TypeId, ) -> Result { - self.check_bid(block)?; self.check_tid(result_type)?; - for &arg in args { - self.check_vid(arg)?; - } - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::UnknownWith { result_type }, args) } fn add_call( @@ -749,60 +925,82 @@ impl ExprContext for ExprBuilder { callee: FuncName, arg: ValueId, ) -> Result { - self.check_bid(block)?; - self.check_vid(arg)?; - match self.callee_spec_vars.entry(callee_spec_var.into()) { - Entry::Occupied(entry) => { - return Err(ErrorKind::DuplicateCalleeSpecVar(entry.key().clone()).into()); - } - Entry::Vacant(entry) => { - entry.insert(hash_func_name(callee_mod, callee)); - } - } - Ok(ValueId(self.vid_gen.next())) + let callee_spec_var_id = self + .callee_spec_vars + .insert(callee_spec_var.into()) + .map_err(|_| ErrorKind::DuplicateCalleeSpecVar(callee_spec_var.into()))?; + self.add_node_checked( + block, + Op::Call { + callee_spec_var: callee_spec_var_id, + callee_mod: callee_mod.into(), + callee: callee.into(), + }, + &[arg], + ) } fn add_const_ref( &mut self, block: BlockId, - _const_mod: ModName, - _const: ConstName, + const_mod: ModName, + const_: ConstName, ) -> Result { - self.check_bid(block)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked( + block, + Op::ConstRef { + const_mod: const_mod.into(), + const_: const_.into(), + }, + &[], + ) } fn add_choice(&mut self, block: BlockId, cases: &[BlockExpr]) -> Result { - self.check_bid(block)?; for &BlockExpr(case_block, case_ret) in cases { self.attach_block(case_block)?; self.check_vid(case_ret)?; } - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked( + block, + Op::Choice { + cases: SmallVec::from_slice(cases), + }, + &[], + ) } fn add_sub_block(&mut self, block: BlockId, sub_block: BlockExpr) -> Result { - self.check_bid(block)?; self.attach_block(sub_block.0)?; self.check_vid(sub_block.1)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::SubBlock { sub_block }, &[]) } - fn add_terminate(&mut self, block: BlockId, result_type: TypeId) -> Result { - self.check_bid(block)?; - self.check_tid(result_type)?; - Ok(ValueId(self.vid_gen.next())) + fn add_terminate( + &mut self, + block: BlockId, + unreachable_result_type: TypeId, + ) -> Result { + self.check_tid(unreachable_result_type)?; + self.add_node_checked( + block, + Op::Terminate { + unreachable_result_type, + }, + &[], + ) } fn add_new_heap_cell(&mut self, block: BlockId) -> Result { - self.check_bid(block)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::NewHeapCell, &[]) } fn add_touch(&mut self, block: BlockId, heap_cell: ValueId) -> Result { - self.check_bid(block)?; - self.check_vid(heap_cell)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::Touch, &[heap_cell]) + } + + fn add_recursive_touch(&mut self, block: BlockId, object: ValueId) -> Result { + self.add_node_checked(block, Op::RecursiveTouch, &[object]) } fn add_update( @@ -821,18 +1019,22 @@ impl ExprContext for ExprBuilder { update_mode_var: UpdateModeVar, heap_cell: ValueId, ) -> Result { - self.check_bid(block)?; - self.check_vid(heap_cell)?; - if !self.update_mode_vars.insert(update_mode_var.into()) { - return Err(ErrorKind::DuplicateUpdateModeVar(update_mode_var.into()).into()); - } - Ok(ValueId(self.vid_gen.next())) + let update_mode_var_id = self + .update_mode_vars + .insert(update_mode_var.into()) + .map_err(|_| ErrorKind::DuplicateUpdateModeVar(update_mode_var.into()))?; + self.add_node_checked( + block, + Op::UpdateWriteOnly { + update_mode_var: update_mode_var_id, + }, + &[heap_cell], + ) } fn add_empty_bag(&mut self, block: BlockId, item_type: TypeId) -> Result { - self.check_bid(block)?; self.check_tid(item_type)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::EmptyBag { item_type }, &[]) } fn add_bag_insert( @@ -841,100 +1043,101 @@ impl ExprContext for ExprBuilder { bag: ValueId, to_insert: ValueId, ) -> Result { - self.check_bid(block)?; - self.check_vid(bag)?; - self.check_vid(to_insert)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::BagInsert, &[bag, to_insert]) } fn add_bag_get(&mut self, block: BlockId, bag: ValueId) -> Result { - self.check_bid(block)?; - self.check_vid(bag)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::BagGet, &[bag]) } fn add_bag_remove(&mut self, block: BlockId, bag: ValueId) -> Result { - self.check_bid(block)?; - self.check_vid(bag)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::BagRemove, &[bag]) } fn add_make_tuple(&mut self, block: BlockId, field_vals: &[ValueId]) -> Result { - self.check_bid(block)?; - for &field_val in field_vals { - self.check_vid(field_val)?; - } - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::MakeTuple, field_vals) } fn add_get_tuple_field( &mut self, block: BlockId, tuple: ValueId, - _field_idx: u32, + field_idx: u32, ) -> Result { - self.check_bid(block)?; - self.check_vid(tuple)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::GetTupleField { field_idx }, &[tuple]) } fn add_make_union( &mut self, block: BlockId, variant_types: &[TypeId], - _variant_idx: u32, + variant_idx: u32, to_wrap: ValueId, ) -> Result { - self.check_bid(block)?; for &variant_type in variant_types { self.check_tid(variant_type)?; } // TODO: Check variant_idx in range - self.check_vid(to_wrap)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked( + block, + Op::MakeUnion { + variant_types: SmallVec::from_slice(variant_types), + variant_idx, + }, + &[to_wrap], + ) } fn add_unwrap_union( &mut self, block: BlockId, to_unwrap: ValueId, - _variant_idx: u32, + variant_idx: u32, ) -> Result { - self.check_bid(block)?; - self.check_vid(to_unwrap)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked(block, Op::UnwrapUnion { variant_idx }, &[to_unwrap]) } fn add_make_named( &mut self, block: BlockId, - _named_mod: ModName, - _named: TypeName, + named_mod: ModName, + named: TypeName, to_wrap: ValueId, ) -> Result { - self.check_bid(block)?; - self.check_vid(to_wrap)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked( + block, + Op::MakeNamed { + named_mod: named_mod.into(), + named: named.into(), + }, + &[to_wrap], + ) } fn add_unwrap_named( &mut self, block: BlockId, - _named_mod: ModName, - _named: TypeName, + named_mod: ModName, + named: TypeName, to_unwrap: ValueId, ) -> Result { - self.check_bid(block)?; - self.check_vid(to_unwrap)?; - Ok(ValueId(self.vid_gen.next())) + self.add_node_checked( + block, + Op::UnwrapNamed { + named_mod: named_mod.into(), + named: named.into(), + }, + &[to_unwrap], + ) } } /// A `FuncDef` defines the signature and body of a function. pub struct FuncDef { - // We can precompute hashes for all calls because monomorphization isn't implemented yet. - callee_spec_vars: BTreeMap, - update_mode_vars: BTreeSet, + builder: FuncDefBuilder, + arg_type: TypeId, + ret_type: TypeId, + root: BlockExpr, } pub struct FuncDefBuilder { @@ -974,17 +1177,19 @@ impl FuncDefBuilder { self.expr_builder.check_tid(ret_type)?; self.expr_builder.finalize_with_root(root)?; Ok(FuncDef { - callee_spec_vars: self.expr_builder.callee_spec_vars, - update_mode_vars: self.expr_builder.update_mode_vars, + builder: self, + arg_type, + ret_type, + root, }) } } /// A `ConstDef` defines the type and initializer expression for a global constant. pub struct ConstDef { - // We can precompute hashes for all calls because monomorphization isn't implemented yet. - callee_spec_vars: BTreeMap, - update_mode_vars: BTreeSet, + builder: ConstDefBuilder, + type_: TypeId, + root: BlockExpr, } pub struct ConstDefBuilder { @@ -1011,8 +1216,9 @@ impl ConstDefBuilder { self.expr_builder.check_tid(type_)?; self.expr_builder.finalize_with_root(root)?; Ok(ConstDef { - callee_spec_vars: self.expr_builder.callee_spec_vars, - update_mode_vars: self.expr_builder.update_mode_vars, + builder: self, + type_, + root, }) } } @@ -1154,14 +1360,21 @@ pub struct FuncSpec(pub [u8; SPEC_HASH_BYTES]); /// The solution table for an individual specialization. pub struct FuncSpecSolutions { func_def: FuncDef, + callee_specs: IdVec, } impl FuncSpecSolutions { pub fn callee_spec(&self, var: CalleeSpecVar) -> Result { // TODO: The clone here is unnecessary -- avoid it! // (might require something like a transmute) - match self.func_def.callee_spec_vars.get(&var.into()) { - Some(spec) => Ok(*spec), + match self + .func_def + .builder + .expr_builder + .callee_spec_vars + .get_by_val(&var.into()) + { + Some(id) => Ok(self.callee_specs[id]), None => Err(ErrorKind::CalleeSpecVarNotFound(var.into()).into()), } } @@ -1169,10 +1382,16 @@ impl FuncSpecSolutions { pub fn update_mode(&self, var: UpdateModeVar) -> Result { // TODO: The clone here is unnecessary -- avoid it! // (might require something like a transmute) - if !self.func_def.update_mode_vars.contains(&var.into()) { - return Err(ErrorKind::UpdateModeVarNotFound(var.into()).into()); + match self + .func_def + .builder + .expr_builder + .update_mode_vars + .get_by_val(&var.into()) + { + Some(_id) => Ok(UpdateMode::Immutable), + None => Err(ErrorKind::UpdateModeVarNotFound(var.into()).into()), } - Ok(UpdateMode::Immutable) } } @@ -1201,14 +1420,21 @@ impl FuncSolutions { /// Note that, unlike functions, constant definitions cannot have multiple specializations. pub struct ConstSolutions { const_def: ConstDef, + callee_specs: IdVec, } impl ConstSolutions { pub fn callee_spec(&self, var: CalleeSpecVar) -> Result { // TODO: The clone here is unnecessary -- avoid it! // (might require something like a transmute) - match self.const_def.callee_spec_vars.get(&var.into()) { - Some(spec) => Ok(*spec), + match self + .const_def + .builder + .expr_builder + .callee_spec_vars + .get_by_val(&var.into()) + { + Some(id) => Ok(self.callee_specs[id]), None => Err(ErrorKind::CalleeSpecVarNotFound(var.into()).into()), } } @@ -1216,10 +1442,16 @@ impl ConstSolutions { pub fn update_mode(&self, var: UpdateModeVar) -> Result { // TODO: The clone here is unnecessary -- avoid it! // (might require something like a transmute) - if !self.const_def.update_mode_vars.contains(&var.into()) { - return Err(ErrorKind::UpdateModeVarNotFound(var.into()).into()); + match self + .const_def + .builder + .expr_builder + .update_mode_vars + .get_by_val(&var.into()) + { + Some(_id) => Ok(UpdateMode::Immutable), + None => Err(ErrorKind::UpdateModeVarNotFound(var.into()).into()), } - Ok(UpdateMode::Immutable) } } @@ -1280,6 +1512,35 @@ impl Solutions { } } +fn populate_specs( + callee_spec_var_ids: Count, + vals: &OpGraph, +) -> IdVec { + let mut results = IdVec::filled_with(callee_spec_var_ids, || None); + for val_id in vals.count().iter() { + match vals.node(val_id) { + Node { + op: + Op::Call { + callee_spec_var, + callee_mod, + callee, + }, + inputs: _, + } => { + replace_none( + &mut results[callee_spec_var], + hash_func_name(callee_mod.borrowed(), callee.borrowed()), + ) + .unwrap(); + } + + _ => {} + } + } + results.into_mapped(|_, spec| spec.unwrap()) +} + pub fn solve(program: Program) -> Result { Ok(Solutions { mods: program @@ -1291,9 +1552,16 @@ pub fn solve(program: Program) -> Result { .func_defs .into_iter() .map(|(func_name, func_def)| { + let callee_specs = populate_specs( + func_def.builder.expr_builder.callee_spec_vars.count(), + &func_def.builder.expr_builder.vals, + ); let func_sols = FuncSolutions { spec: hash_func_name(mod_name.borrowed(), func_name.borrowed()), - spec_solutions: FuncSpecSolutions { func_def }, + spec_solutions: FuncSpecSolutions { + func_def, + callee_specs, + }, }; (func_name, func_sols) }) @@ -1301,7 +1569,19 @@ pub fn solve(program: Program) -> Result { consts: mod_def .const_defs .into_iter() - .map(|(const_name, const_def)| (const_name, ConstSolutions { const_def })) + .map(|(const_name, const_def)| { + let callee_specs = populate_specs( + const_def.builder.expr_builder.callee_spec_vars.count(), + &const_def.builder.expr_builder.vals, + ); + ( + const_name, + ConstSolutions { + const_def, + callee_specs, + }, + ) + }) .collect(), }; (mod_name, mod_sols) diff --git a/vendor/morphic_lib/src/bindings.rs b/vendor/morphic_lib/src/bindings.rs index c8864e30d9..72ce812cf8 100644 --- a/vendor/morphic_lib/src/bindings.rs +++ b/vendor/morphic_lib/src/bindings.rs @@ -218,27 +218,28 @@ pub unsafe extern "C" fn Morphic_FuncDefBuilder_Build( } #[no_mangle] -pub unsafe extern "C" fn Morphic_FuncDefBuilder_DeclareJoinPoint( +pub unsafe extern "C" fn Morphic_FuncDefBuilder_DeclareContinuation( self_: *mut FuncDefBuilder, block: BlockId, arg_type: TypeId, ret_type: TypeId, - out0: *mut JoinPointId, + out0: *mut ContinuationId, out1: *mut ValueId, ) -> *mut Error { - let (join_point, value) = check_err!((*self_).declare_join_point(block, arg_type, ret_type)); - *out0 = join_point; + let (continuation, value) = + check_err!((*self_).declare_continuation(block, arg_type, ret_type)); + *out0 = continuation; *out1 = value; ptr::null_mut() } #[no_mangle] -pub unsafe extern "C" fn Morphic_FuncDefBuilder_DefineJoinPoint( +pub unsafe extern "C" fn Morphic_FuncDefBuilder_DefineContinuation( self_: *mut FuncDefBuilder, - join_point: JoinPointId, + continuation: ContinuationId, body: BlockExpr, ) -> *mut Error { - check_err!((*self_).define_join_point(join_point, body)); + check_err!((*self_).define_continuation(continuation, body)); ptr::null_mut() } @@ -246,12 +247,12 @@ pub unsafe extern "C" fn Morphic_FuncDefBuilder_DefineJoinPoint( pub unsafe extern "C" fn Morphic_FuncDefBuilder_AddJump( self_: *mut FuncDefBuilder, block: BlockId, - join_point: JoinPointId, + continuation: ContinuationId, arg: ValueId, unreachable_result_type: TypeId, out: *mut ValueId, ) -> *mut Error { - *out = check_err!((*self_).add_jump(block, join_point, arg, unreachable_result_type)); + *out = check_err!((*self_).add_jump(block, continuation, arg, unreachable_result_type)); ptr::null_mut() } diff --git a/vendor/morphic_lib/src/util/bytes_id.rs b/vendor/morphic_lib/src/util/bytes_id.rs index a5168c4c71..328fba47b8 100644 --- a/vendor/morphic_lib/src/util/bytes_id.rs +++ b/vendor/morphic_lib/src/util/bytes_id.rs @@ -4,13 +4,15 @@ macro_rules! bytes_id { $(#[$annot_borrowed:meta])* $borrowed_vis:vis $borrowed:ident; $(#[$annot_owned:meta])* $owned_vis:vis $owned:ident; ) => { - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] $(#[$annot_borrowed])* $borrowed_vis struct $borrowed<'a>($borrowed_vis &'a [u8]); - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + // The stack-allocated array portion of a `SmallVec` shares a union with two `usize`s, so on + // 64-bit platforms we can make the array up to 16 bytes long with no space penalty. + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] $(#[$annot_owned])* - $owned_vis struct $owned($owned_vis ::std::vec::Vec); + $owned_vis struct $owned($owned_vis ::smallvec::SmallVec<[u8; 16]>); impl $owned { fn borrowed<'a>(&'a self) -> $borrowed<'a> { @@ -20,7 +22,7 @@ macro_rules! bytes_id { impl<'a> ::std::convert::From<$borrowed<'a>> for $owned { fn from(borrowed: $borrowed<'a>) -> Self { - $owned(<[u8]>::to_vec(&borrowed.0)) + $owned(::smallvec::SmallVec::from_slice(&borrowed.0)) } } } diff --git a/vendor/morphic_lib/src/util/mod.rs b/vendor/morphic_lib/src/util/mod.rs index a58e87cdac..b382f997cd 100644 --- a/vendor/morphic_lib/src/util/mod.rs +++ b/vendor/morphic_lib/src/util/mod.rs @@ -1,5 +1,14 @@ +#[macro_use] +pub mod id_type; + #[macro_use] pub mod bytes_id; #[macro_use] pub mod forward_trait; + +pub mod blocks; +pub mod id_bi_map; +pub mod id_vec; +pub mod op_graph; +pub mod replace_none;