diff --git a/fix-codegen/src/disassembler.rs b/fix-codegen/src/disassembler.rs index e8052ff..57aa4c4 100644 --- a/fix-codegen/src/disassembler.rs +++ b/fix-codegen/src/disassembler.rs @@ -255,7 +255,9 @@ impl<'a, Ctx: DisassemblerContext> Disassembler<'a, Ctx> { ("MakePatternClosure", arg_str) } - Op::Call => ("Call", String::new()), + Op::Call => { + ("Call", String::new()) + }, Op::MakeAttrs => { let count = self.read_u32(); diff --git a/fix-codegen/src/lib.rs b/fix-codegen/src/lib.rs index af9eab2..5192487 100644 --- a/fix-codegen/src/lib.rs +++ b/fix-codegen/src/lib.rs @@ -1,8 +1,8 @@ use std::ops::Deref; -use fix_builtins::{BUILTINS, BuiltinId}; +use fix_builtins::BuiltinId; use fix_common::StringId; -use fix_ir::{Attr, BinOpKind, Ir, Param, RawIrRef, ThunkId, UnOpKind}; +use fix_ir::{Attr, BinOpKind, Ir, MaybeThunk, Param, RawIrRef, ThunkId, UnOpKind}; use hashbrown::HashMap; use num_enum::TryFromPrimitive; use rnix::TextRange; @@ -151,40 +151,35 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { } } - fn classify_value(&mut self, ir: RawIrRef<'_>) -> InlineOperand { - match ir.deref() { - &Ir::Int(x) => { + #[must_use] + fn inline_maybe_thunk(&mut self, val: MaybeThunk) -> InlineOperand { + use MaybeThunk::*; + match val { + Int(x) => { if x <= i32::MAX as i64 { InlineOperand::Const(Const::Smi(x as i32)) } else { InlineOperand::BigInt(x) } } - &Ir::Float(x) => InlineOperand::Const(Const::Float(x)), - &Ir::Bool(b) => InlineOperand::Const(Const::Bool(b)), - Ir::Null => InlineOperand::Const(Const::Null), - Ir::Str(s) => { - let sid = self.ctx.intern_string(s.deref()); - InlineOperand::Const(Const::String(sid)) - } - &Ir::Thunk(id) => { + Float(x) => InlineOperand::Const(Const::Float(x)), + Bool(b) => InlineOperand::Const(Const::Bool(b)), + Null => InlineOperand::Const(Const::Null), + Str(id) => InlineOperand::Const(Const::String(id)), + Thunk(id) => { let (layer, local) = self.resolve_thunk(id); InlineOperand::Local { layer, local } } - &Ir::Arg { layer } => InlineOperand::Local { + Arg { layer } => InlineOperand::Local { layer: layer.try_into().expect("scope too deep!"), local: 0, }, - &Ir::Builtin(id) => { - let arity = BUILTINS[id as usize].1; - InlineOperand::Const(Const::PrimOp { id, arity }) - } - Ir::Builtins => InlineOperand::Builtins, - _ => panic!("cannot classify IR node as inline operand"), + _ => todo!(), } } - fn emit_inline_operand(&mut self, operand: InlineOperand) { + fn emit_maybe_thunk(&mut self, val: MaybeThunk) { + let operand = self.inline_maybe_thunk(val); match operand { InlineOperand::Const(val) => { let idx = self.ctx.add_constant(val); @@ -320,30 +315,15 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { self.count_with_thunks(*lhs) + self.count_with_thunks(*rhs) } Ir::UnOp { rhs, .. } => self.count_with_thunks(*rhs), - Ir::Call { func, arg, .. } => { - self.count_with_thunks(*func) + self.count_with_thunks(*arg) - } + Ir::Call { func, .. } => self.count_with_thunks(*func), Ir::Assert { assertion, expr, .. } => self.count_with_thunks(*assertion) + self.count_with_thunks(*expr), - Ir::Select { expr, default, .. } => { - self.count_with_thunks(*expr) + default.map_or(0, |d| self.count_with_thunks(d)) - } + Ir::Select { expr, .. } => self.count_with_thunks(*expr), Ir::HasAttr { lhs, .. } => self.count_with_thunks(*lhs), Ir::ConcatStrings { parts, .. } => { parts.iter().map(|p| self.count_with_thunks(*p)).sum() } - Ir::Path(p) => self.count_with_thunks(*p), - Ir::List { items } => items.iter().map(|item| self.count_with_thunks(*item)).sum(), - Ir::AttrSet { stcs, dyns } => { - stcs.iter() - .map(|(_, &(val, _))| self.count_with_thunks(val)) - .sum::() - + dyns - .iter() - .map(|&(k, v, _)| self.count_with_thunks(k) + self.count_with_thunks(v)) - .sum::() - } _ => 0, } } @@ -392,9 +372,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { self.collect_with_thunks_recursive(*rhs, out); } Ir::UnOp { rhs, .. } => self.collect_with_thunks_recursive(*rhs, out), - Ir::Call { func, arg, .. } => { + Ir::Call { func, .. } => { self.collect_with_thunks_recursive(*func, out); - self.collect_with_thunks_recursive(*arg, out); } Ir::Assert { assertion, expr, .. @@ -402,11 +381,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { self.collect_with_thunks_recursive(*assertion, out); self.collect_with_thunks_recursive(*expr, out); } - Ir::Select { expr, default, .. } => { + Ir::Select { expr, .. } => { self.collect_with_thunks_recursive(*expr, out); - if let Some(d) = default { - self.collect_with_thunks_recursive(*d, out); - } } Ir::HasAttr { lhs, .. } => self.collect_with_thunks_recursive(*lhs, out), Ir::ConcatStrings { parts, .. } => { @@ -414,22 +390,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { self.collect_with_thunks_recursive(*p, out); } } - Ir::Path(p) => self.collect_with_thunks_recursive(*p, out), - Ir::List { items } => { - for item in items.iter() { - self.collect_with_thunks_recursive(*item, out); - } - } - Ir::AttrSet { stcs, dyns } => { - for (_, &(val, _)) in stcs.iter() { - self.collect_with_thunks_recursive(val, out); - } - for &(key, val, _) in dyns.iter() { - self.collect_with_thunks_recursive(key, out); - self.collect_with_thunks_recursive(val, out); - } - } - _ => {} + _ => (), } } @@ -512,10 +473,9 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { &Ir::Bool(true) => self.emit_op(Op::PushTrue), &Ir::Bool(false) => self.emit_op(Op::PushFalse), Ir::Null => self.emit_op(Op::PushNull), - Ir::Str(s) => { - let idx = self.ctx.intern_string(s.deref()); + &Ir::Str(id) => { self.emit_op(Op::PushString); - self.emit_str_id(idx); + self.emit_str_id(id); } &Ir::Path(p) => { self.emit_expr(p); @@ -572,15 +532,14 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { self.emit_op(Op::MakeList); self.emit_u32(items.len() as u32); for &item in items.iter() { - let operand = self.classify_value(item); - self.emit_inline_operand(operand); + self.emit_maybe_thunk(item); } } } &Ir::Call { func, arg, .. } => { - self.emit_expr(arg); self.emit_expr(func); self.emit_op(Op::Call); + self.emit_maybe_thunk(arg); } &Ir::Arg { layer } => { self.emit_load(layer.try_into().expect("scope too deep!"), 0); @@ -616,16 +575,17 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { ); } &Ir::ConcatStrings { - ref parts, - force_string, + parts: _, + force_string: _, } => { - self.emit_op(Op::ConcatStrings); - self.emit_u16(parts.len() as u16); - self.emit_u8(if force_string { 1 } else { 0 }); - for &part in parts.iter() { - let operand = self.classify_value(part); - self.emit_inline_operand(operand); - } + todo!("redesign ConcatStrings"); + // self.emit_op(Op::ConcatStrings); + // self.emit_u16(parts.len() as u16); + // self.emit_u8(if force_string { 1 } else { 0 }); + // for &part in parts.iter() { + // let operand = self.inline_maybe_thunk(part); + // self.emit_inline_operand(operand); + // } } &Ir::HasAttr { lhs, ref rhs } => { self.emit_has_attr(lhs, rhs); @@ -732,14 +692,16 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { self.patch_i32(end_placeholder, end_offset); } PipeL => { - self.emit_expr(rhs); - self.emit_expr(lhs); - self.emit_op(Op::Call); + todo!("new call"); + // self.emit_expr(rhs); + // self.emit_expr(lhs); + // self.emit_op(Op::Call); } PipeR => { - self.emit_expr(lhs); - self.emit_expr(rhs); - self.emit_op(Op::Call); + todo!("new call"); + // self.emit_expr(lhs); + // self.emit_expr(rhs); + // self.emit_op(Op::Call); } _ => { self.emit_expr(lhs); @@ -817,8 +779,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { fn emit_attrset( &mut self, - stcs: &fix_ir::HashMap<'_, StringId, (RawIrRef<'_>, TextRange)>, - dyns: &[(RawIrRef<'_>, RawIrRef<'_>, TextRange)], + stcs: &fix_ir::HashMap<'_, StringId, (MaybeThunk, TextRange)>, + dyns: &[(RawIrRef<'_>, MaybeThunk, TextRange)], ) { if stcs.is_empty() && dyns.is_empty() { self.emit_op(Op::MakeEmptyAttrs); @@ -832,19 +794,18 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { for (&sym, &(val, span)) in stcs.iter() { self.emit_u8(AttrKeyType::Static as u8); self.emit_str_id(sym); - let val_operand = self.classify_value(val); - self.emit_inline_operand(val_operand); + self.emit_maybe_thunk(val); let span_id = self.ctx.register_span(span); self.emit_u32(span_id); } - for &(key, val, span) in dyns.iter() { - self.emit_u8(AttrKeyType::Dynamic as u8); - let key_operand = self.classify_value(key); - self.emit_inline_operand(key_operand); - let val_operand = self.classify_value(val); - self.emit_inline_operand(val_operand); - let span_id = self.ctx.register_span(span); - self.emit_u32(span_id); + for &(_key, _val, _span) in dyns.iter() { + todo!("redesign dynamic attr key"); + // self.emit_u8(AttrKeyType::Dynamic as u8); + // self.emit_maybe_thunk(key); + // let val_operand = self.inline_maybe_thunk(val); + // self.emit_maybe_thunk(val); + // let span_id = self.ctx.register_span(span); + // self.emit_u32(span_id); } } @@ -910,12 +871,12 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> { fn emit_with( &mut self, - namespace: RawIrRef<'_>, + namespace: MaybeThunk, body: RawIrRef<'_>, thunks: &[(ThunkId, RawIrRef<'_>)], ) { - self.emit_expr(namespace); self.emit_op(Op::PushWith); + self.emit_maybe_thunk(namespace); self.emit_scope_thunks(thunks); self.emit_expr(body); self.emit_op(Op::PopWith); diff --git a/fix-ir/src/downgrade.rs b/fix-ir/src/downgrade.rs index e103f46..66e4b71 100644 --- a/fix-ir/src/downgrade.rs +++ b/fix-ir/src/downgrade.rs @@ -1,4 +1,3 @@ -use bumpalo::boxed::Box; use bumpalo::collections::{CollectIn, Vec}; use fix_builtins::BuiltinId; use fix_common::Symbol; @@ -6,7 +5,7 @@ use fix_error::{Error, Result, Source}; use hashbrown::HashSet; use hashbrown::hash_map::Entry; use rnix::TextRange; -use rnix::ast::{self, Expr, HasEntry}; +use rnix::ast::{self, AstToken, Expr, HasEntry}; use rowan::ast::AstNode; use super::*; @@ -39,20 +38,13 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>, T, E: std::fmt::Display> } } -trait BoxIn: Sized { - fn box_in(self, bump: &bumpalo::Bump) -> Box<'_, Self> { - Box::new_in(self, bump) - } -} -impl BoxIn for T {} - pub trait DowngradeContext<'id: 'ir, 'ir> { fn new_expr(&self, expr: Ir<'ir, IrRef<'id, 'ir>>) -> IrRef<'id, 'ir>; - fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> IrRef<'id, 'ir>; + fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> MaybeThunk; - fn new_sym(&mut self, sym: String) -> StringId; - fn get_sym(&self, id: StringId) -> Symbol<'_>; - fn lookup(&self, sym: StringId, span: TextRange) -> Result>; + fn intern_string(&mut self, sym: impl AsRef) -> StringId; + fn resolve_sym(&self, id: StringId) -> Symbol<'_>; + fn lookup(&self, sym: StringId, span: TextRange) -> Result; fn get_current_source(&self) -> Source; @@ -165,14 +157,15 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo let path = { let temp = self.content().require(ctx, span)?; let text = temp.text(); - ctx.new_expr(Ir::Str( - text[1..text.len() - 1].to_string().box_in(ctx.bump()), - )) + let id = ctx.intern_string(&text[1..text.len() - 1]); + let expr = ctx.new_expr(Ir::Str(id)); + ctx.maybe_thunk(expr) }; // HACK: disgusting eww let find_file = ctx.new_expr(Ir::Builtin(BuiltinId::FindFile)); - let sym = ctx.new_sym("nixPath".into()); + let sym = ctx.intern_string("nixPath"); let nix_path = ctx.new_expr(Ir::BuiltinConst(sym)); + let nix_path = ctx.maybe_thunk(nix_path); let call = ctx.new_expr(Ir::Call { func: find_file, arg: nix_path, @@ -194,14 +187,14 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo let bump = ctx.bump(); let mut parts = normalized.into_iter().map(|part| match part { - ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr(Ir::Str(lit.box_in(bump)))), - ast::InterpolPart::Interpolation(interpol) => { - let inner = interpol - .expr() - .require(ctx, interpol.syntax().text_range())? - .downgrade(ctx)?; - Ok(ctx.maybe_thunk(inner)) + ast::InterpolPart::Literal(lit) => { + let id = ctx.intern_string(lit); + Ok(ctx.new_expr(Ir::Str(id))) } + ast::InterpolPart::Interpolation(interpol) => interpol + .expr() + .require(ctx, interpol.syntax().text_range())? + .downgrade(ctx), }); Ok(if is_single_literal { @@ -219,11 +212,15 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> for ast::Literal { fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); - Ok(ctx.new_expr(match self.kind() { + let expr = match self.kind() { ast::LiteralKind::Integer(int) => Ir::Int(int.value().require(ctx, span)?), ast::LiteralKind::Float(float) => Ir::Float(float.value().require(ctx, span)?), - ast::LiteralKind::Uri(uri) => Ir::Str(uri.to_string().box_in(ctx.bump())), - })) + ast::LiteralKind::Uri(uri) => { + let id = ctx.intern_string(uri.syntax().text()); + Ir::Str(id) + } + }; + Ok(ctx.new_expr(expr)) } } @@ -231,8 +228,8 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo fn downgrade(self, ctx: &mut Ctx) -> Result> { let span = self.syntax().text_range(); let text = self.ident_token().require(ctx, span)?.to_string(); - let sym = ctx.new_sym(text); - ctx.lookup(sym, span) + let sym = ctx.intern_string(text); + ctx.lookup(sym, span).map(|thunk| thunk.to_ir(ctx)) } } @@ -299,12 +296,10 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo let span = self.syntax().text_range(); let expr = self.expr().require(ctx, span)?.downgrade(ctx)?; let attrpath = downgrade_attrpath(self.attrpath().require(ctx, span)?, ctx)?; - let default = if let Some(default) = self.default_expr() { - let default_expr = default.downgrade(ctx)?; - Some(ctx.maybe_thunk(default_expr)) - } else { - None - }; + let default = self + .default_expr() + .map(|expr| expr.downgrade(ctx)) + .transpose()?; let span = self.syntax().text_range(); Ok(ctx.new_expr(Ir::Select { expr, @@ -331,7 +326,7 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo Ok(ctx.new_expr(Ir::AttrSet { stcs, dyns })) })?; - let body_sym = ctx.new_sym("body".to_string()); + let body_sym = ctx.intern_string("body"); Ok(ctx.new_expr(Ir::Select { expr: attrset_expr, attrpath: Vec::from_iter_in( @@ -390,7 +385,7 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo match raw_param { ast::Param::IdentParam(id) => { - let param_sym = ctx.new_sym(id.to_string()); + let param_sym = ctx.intern_string(id.to_string()); param = None; body = ctx.with_param_scope(param_sym, |ctx| body_ast.downgrade(ctx))?; @@ -400,7 +395,7 @@ impl<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>> Downgrade<'id, 'ir, Ctx> fo .pat_bind() .map(|alias| { let ident = alias.ident().require(ctx, alias.syntax().text_range())?; - Ok::<_, std::boxed::Box>(ctx.new_sym(ident.to_string())) + Ok::<_, std::boxed::Box>(ctx.intern_string(ident.to_string())) }) .transpose()?; @@ -484,7 +479,7 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> { match first { ast::Attr::Ident(ident) => { - let sym = ctx.new_sym(ident.to_string()); + let sym = ctx.intern_string(ident.to_string()); self.insert_static(sym, span, rest, value, ctx) } ast::Attr::Str(string) => { @@ -493,7 +488,7 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> { && let ast::InterpolPart::Literal(lit) = parts.into_iter().next().expect("len checked") { - let sym = ctx.new_sym(lit); + let sym = ctx.intern_string(lit); return self.insert_static(sym, span, rest, value, ctx); } self.insert_dynamic(first.clone(), span, rest, value, ctx) @@ -736,14 +731,14 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> { for attr in inherit.attrs() { let span = attr.syntax().text_range(); let sym = match &attr { - ast::Attr::Ident(ident) => ctx.new_sym(ident.to_string()), + ast::Attr::Ident(ident) => ctx.intern_string(ident.to_string()), ast::Attr::Str(s) => { let parts = s.normalized_parts(); if parts.len() == 1 && let ast::InterpolPart::Literal(lit) = parts.into_iter().next().expect("len checked") { - ctx.new_sym(lit) + ctx.intern_string(lit) } else { return Err(Error::downgrade_error( "dynamic attributes not allowed in inherit".to_string(), @@ -763,7 +758,7 @@ impl<'id: 'ir, 'ir> PendingAttrSet<'ir> { if self.stcs.contains_key(&sym) { return Err(Error::downgrade_error( - format!("attribute '{}' already defined", ctx.get_sym(sym)), + format!("attribute '{}' already defined", ctx.resolve_sym(sym)), ctx.get_current_source(), span, )); @@ -840,8 +835,8 @@ fn make_attrpath_value_entry<'ir>(path: Vec<'ir, ast::Attr>, value: ast::Expr) - } struct FinalizedAttrSet<'id, 'ir> { - stcs: HashMap<'ir, StringId, (IrRef<'id, 'ir>, TextRange)>, - dyns: Vec<'ir, (IrRef<'id, 'ir>, IrRef<'id, 'ir>, TextRange)>, + stcs: HashMap<'ir, StringId, (MaybeThunk, TextRange)>, + dyns: Vec<'ir, (IrRef<'id, 'ir>, MaybeThunk, TextRange)>, } fn downgrade_attrs<'id, 'ir>( @@ -861,17 +856,17 @@ fn downgrade_attr<'id, 'ir>( use ast::InterpolPart::*; match attr { Ident(ident) => Ok(Attr::Str( - ctx.new_sym(ident.to_string()), + ctx.intern_string(ident.to_string()), ident.syntax().text_range(), )), Str(string) => { let parts = string.normalized_parts(); let span = string.syntax().text_range(); if parts.is_empty() { - Ok(Attr::Str(ctx.new_sym("".to_string()), span)) + Ok(Attr::Str(ctx.intern_string(""), span)) } else if parts.len() == 1 { match parts.into_iter().next().expect("len checked") { - Literal(ident) => Ok(Attr::Str(ctx.new_sym(ident), span)), + Literal(ident) => Ok(Attr::Str(ctx.intern_string(ident), span)), Interpolation(interpol) => Ok(Attr::Dynamic( interpol .expr() @@ -885,7 +880,10 @@ fn downgrade_attr<'id, 'ir>( let parts = parts .into_iter() .map(|part| match part { - Literal(lit) => Ok(ctx.new_expr(Ir::Str(lit.box_in(bump)))), + Literal(lit) => { + let id = ctx.intern_string(lit); + Ok(ctx.new_expr(Ir::Str(id))) + } Interpolation(interpol) => interpol .expr() .require(ctx, interpol.syntax().text_range())? @@ -950,13 +948,13 @@ where for entry in pat_entries { let ident = entry.ident().require(ctx, entry.syntax().text_range())?; let sym_span = ident.syntax().text_range(); - let sym = ctx.new_sym(ident.syntax().text().to_string()); + let sym = ctx.intern_string(ident.syntax().text().to_string()); let default = entry.default(); let span = entry.syntax().text_range(); if !seen_params.insert(sym) { return Err(Error::downgrade_error( - format!("duplicate parameter '{}'", ctx.get_sym(sym)), + format!("duplicate parameter '{}'", ctx.resolve_sym(sym)), ctx.get_current_source(), span, )); @@ -1001,12 +999,13 @@ where default, span, } = param; - let default = if let Some(default) = default { - let default = default.clone().downgrade(ctx)?; - Some(ctx.maybe_thunk(default)) - } else { - None - }; + let default = default.map(|default| default.downgrade(ctx)).transpose()?; + // let default = if let Some(default) = default { + // let default = default.clone().downgrade(ctx)?; + // Some(ctx.maybe_thunk(default)) + // } else { + // None + // }; Ok(ctx.new_expr(Ir::Select { expr: arg, @@ -1075,7 +1074,7 @@ where F: FnOnce( &mut Ctx, &[StringId], - &[(IrRef<'id, 'ir>, IrRef<'id, 'ir>, TextRange)], + &[(IrRef<'id, 'ir>, MaybeThunk, TextRange)], ) -> Result>, { let mut pending = PendingAttrSet::new_in(ctx.bump()); @@ -1088,18 +1087,21 @@ where ctx.with_let_scope(&keys, |ctx| { let finalized = finalize_pending_set::<_, ALLOW_DYN>(pending, &inherit_lookups, ctx)?; - let vals = keys - .iter() - .map(|sym| finalized.stcs.get(sym).expect("WTF").0) - .collect_in(ctx.bump()); + let vals = { + let mut temp = Vec::with_capacity_in(keys.len(), ctx.bump()); + for sym in &keys { + temp.push(finalized.stcs.get(sym).expect("WTF").0.to_ir(ctx)); + } + temp + }; body_fn(ctx, &keys, &finalized.dyns).map(|body| (vals, body)) }) } -fn collect_inherit_lookups<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>>( +fn collect_inherit_lookups<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>>( entries: &[ast::Entry], ctx: &mut Ctx, -) -> Result, TextRange)>> { +) -> Result> { let mut inherit_lookups = HashMap::new_in(ctx.bump()); for entry in entries { if let ast::Entry::Inherit(inherit) = entry @@ -1108,7 +1110,7 @@ fn collect_inherit_lookups<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>>( for attr in inherit.attrs() { if let ast::Attr::Ident(ident) = attr { let attr_span = ident.syntax().text_range(); - let sym = ctx.new_sym(ident.to_string()); + let sym = ctx.intern_string(ident.to_string()); let expr = ctx.lookup(sym, attr_span)?; inherit_lookups.insert(sym, (expr, attr_span)); } @@ -1127,7 +1129,7 @@ fn collect_binding_syms<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>, const AL for (sym, (_, span)) in &pending.stcs { if !binding_syms.insert(*sym) { return Err(Error::downgrade_error( - format!("attribute '{}' already defined", ctx.get_sym(*sym)), + format!("attribute '{}' already defined", ctx.resolve_sym(*sym)), ctx.get_current_source(), *span, )); @@ -1139,15 +1141,15 @@ fn collect_binding_syms<'id: 'ir, 'ir, Ctx: DowngradeContext<'id, 'ir>, const AL fn finalize_pending_set<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>( pending: PendingAttrSet, - inherit_lookups: &HashMap, TextRange)>, + inherit_lookups: &HashMap, ctx: &mut Ctx, ) -> Result> { let mut stcs = HashMap::new_in(ctx.bump()); let mut dyns = Vec::new_in(ctx.bump()); for (sym, (value, value_span)) in pending.stcs { - let expr_id = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?; - stcs.insert(sym, (expr_id, value_span)); + let expr = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?; + stcs.insert(sym, (ctx.maybe_thunk(expr), value_span)); } if ALLOW_DYN { @@ -1155,12 +1157,10 @@ fn finalize_pending_set<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_D let key_id = downgrade_attr(attr, ctx)?; let key_expr = match key_id { Attr::Dynamic(id, _) => id, - Attr::Str(sym, _attr_span) => { - ctx.new_expr(Ir::Str(ctx.get_sym(sym).to_string().box_in(ctx.bump()))) - } + Attr::Str(sym, _attr_span) => ctx.new_expr(Ir::Str(sym)), }; - let value_id = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?; - dyns.push((key_expr, value_id, value_span)); + let value = finalize_pending_value::<_, ALLOW_DYN>(value, inherit_lookups, ctx)?; + dyns.push((key_expr, ctx.maybe_thunk(value), value_span)); } } @@ -1169,30 +1169,25 @@ fn finalize_pending_set<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_D fn finalize_pending_value<'id, 'ir, Ctx: DowngradeContext<'id, 'ir>, const ALLOW_DYN: bool>( value: PendingValue, - inherit_lookups: &HashMap, TextRange)>, + inherit_lookups: &HashMap, ctx: &mut Ctx, ) -> Result> { match value { - PendingValue::Expr(expr) => { - let id = Downgrade::downgrade(expr, ctx)?; - Ok(ctx.maybe_thunk(id)) - } + PendingValue::Expr(expr) => expr.downgrade(ctx), PendingValue::InheritFrom(from_expr, sym, span) => { - let from_id = Downgrade::downgrade(from_expr, ctx)?; - let select_id = ctx.new_expr(Ir::Select { - expr: from_id, + let from = Downgrade::downgrade(from_expr, ctx)?; + Ok(ctx.new_expr(Ir::Select { + expr: from, attrpath: Vec::from_iter_in([Attr::Str(sym, span)], ctx.bump()), default: None, span, - }); - Ok(ctx.maybe_thunk(select_id)) + })) } PendingValue::InheritScope(sym, span) => { if let Some(&(expr, _)) = inherit_lookups.get(&sym) { - Ok(ctx.maybe_thunk(expr)) + Ok(expr.to_ir(ctx)) } else { - let lookup_id = ctx.lookup(sym, span)?; - Ok(ctx.maybe_thunk(lookup_id)) + ctx.lookup(sym, span).map(|val| val.to_ir(ctx)) } } PendingValue::Set(set) => { @@ -1221,7 +1216,8 @@ fn downgrade_path<'id, 'ir>( .into_iter() .map(|part| match part { ast::InterpolPart::Literal(lit) => { - Ok(ctx.new_expr(Ir::Str(lit.text().to_string().box_in(ctx.bump())))) + let id = ctx.intern_string(lit.text()); + Ok(ctx.new_expr(Ir::Str(id))) } ast::InterpolPart::Interpolation(interpol) => interpol .expr() diff --git a/fix-ir/src/lib.rs b/fix-ir/src/lib.rs index bc3c76a..0336568 100644 --- a/fix-ir/src/lib.rs +++ b/fix-ir/src/lib.rs @@ -2,7 +2,6 @@ use std::hash::Hash; use std::ops::Deref; use bumpalo::Bump; -use bumpalo::boxed::Box; use bumpalo::collections::Vec; use fix_builtins::{BUILTINS, BuiltinId}; use fix_common::StringId; @@ -11,6 +10,8 @@ use num_enum::TryFromPrimitive as _; use rnix::{TextRange, ast}; use string_interner::DefaultStringInterner; +use crate::downgrade::DowngradeContext; + pub mod downgrade; pub type HashMap<'ir, K, V> = hashbrown::HashMap; @@ -62,6 +63,46 @@ impl<'ir> Deref for RawIrRef<'ir> { } } +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub enum MaybeThunk { + Int(i64), + Float(f64), + Bool(bool), + Null, + Str(StringId), + Path(StringId), + Thunk(ThunkId), + Arg { layer: usize }, + Builtin(BuiltinId), + Builtins, + ReplBinding(StringId), + ScopedImportBinding(StringId), + WithLookup(StringId), +} + +impl MaybeThunk { + fn to_ir<'id, 'ir>(self, ctx: &mut impl DowngradeContext<'id, 'ir>) -> IrRef<'id, 'ir> { + use MaybeThunk::*; + let ir = match self { + Int(x) => Ir::Int(x), + Float(x) => Ir::Float(x), + Bool(x) => Ir::Bool(x), + Null => Ir::Null, + Str(x) => Ir::Str(x), + Path(x) => Ir::Path(ctx.new_expr(Ir::Str(x))), + Thunk(x) => Ir::Thunk(x), + Arg { layer } => Ir::Arg { layer }, + Builtin(x) => Ir::Builtin(x), + Builtins => Ir::Builtins, + ReplBinding(x) => Ir::ReplBinding(x), + ScopedImportBinding(x) => Ir::ScopedImportBinding(x), + WithLookup(x) => Ir::WithLookup(x), + }; + ctx.new_expr(ir) + } +} + #[repr(C)] #[derive(Debug)] pub enum Ir<'ir, Ref> { @@ -69,15 +110,15 @@ pub enum Ir<'ir, Ref> { Float(f64), Bool(bool), Null, - Str(Box<'ir, String>), + Str(StringId), + Path(Ref), AttrSet { - stcs: HashMap<'ir, StringId, (Ref, TextRange)>, - dyns: Vec<'ir, (Ref, Ref, TextRange)>, + stcs: HashMap<'ir, StringId, (MaybeThunk, TextRange)>, + dyns: Vec<'ir, (Ref, MaybeThunk, TextRange)>, }, List { - items: Vec<'ir, Ref>, + items: Vec<'ir, MaybeThunk>, }, - Path(Ref), ConcatStrings { parts: Vec<'ir, Ref>, force_string: bool, @@ -118,7 +159,7 @@ pub enum Ir<'ir, Ref> { }, With { - namespace: Ref, + namespace: MaybeThunk, body: Ref, thunks: Vec<'ir, (ThunkId, Ref)>, }, @@ -135,7 +176,7 @@ pub enum Ir<'ir, Ref> { }, Call { func: Ref, - arg: Ref, + arg: MaybeThunk, span: TextRange, }, diff --git a/fix-vm/src/lib.rs b/fix-vm/src/lib.rs index f8582e0..061229b 100644 --- a/fix-vm/src/lib.rs +++ b/fix-vm/src/lib.rs @@ -413,9 +413,7 @@ impl<'gc> GcRoot<'gc> { let Ok(ty) = AttrKeyType::try_from_primitive(tag) .map_err(|err| panic!("unknown key tag: {:#04x}", err.number)); match ty { - AttrKeyType::Static => { - keys.push(SelectKeyData::Static(read!(StringId))) - } + AttrKeyType::Static => keys.push(SelectKeyData::Static(read!(StringId))), AttrKeyType::Dynamic => keys.push(SelectKeyData::Dynamic), }; } @@ -598,14 +596,15 @@ impl<'gc> GcRoot<'gc> { } Call => { - // force func try_force!(0, inst_start_pc); if self.call_depth > 10000 { - return Action::Done(Err(Error::eval_error("stack overflow; max-call-depth exceeded"))) + return Action::Done(Err(Error::eval_error( + "stack overflow; max-call-depth exceeded", + ))); } self.call_depth += 1; let func = self.pop_stack(); - let arg = self.pop_stack(); + let arg = read!(OperandData).resolve(mc, self); if let Some(closure) = func.as_gc::() { let ip = closure.ip; let n_locals = closure.n_locals; @@ -783,16 +782,17 @@ impl<'gc> GcRoot<'gc> { }; if let Some(attrset) = current_val.as_gc::() - && let Some(v) = attrset.lookup(key_sid) { - if i < n - 1 { - current_val = v.restrict().unwrap_or_else(|_| { - panic!("intermediate select values must be forced") - }); - } else { - result_val = Some(v); - } - continue; + && let Some(v) = attrset.lookup(key_sid) + { + if i < n - 1 { + current_val = v.restrict().unwrap_or_else(|_| { + panic!("intermediate select values must be forced") + }); + } else { + result_val = Some(v); } + continue; + } use_default = true; break; @@ -992,7 +992,7 @@ impl<'gc> GcRoot<'gc> { } PushWith => { - let env = self.pop_stack(); + let env = read!(OperandData).resolve(mc, self); let scope = Gc::new( mc, WithEnv { diff --git a/fix/src/lib.rs b/fix/src/lib.rs index fafcc52..ce6bb2e 100644 --- a/fix/src/lib.rs +++ b/fix/src/lib.rs @@ -7,7 +7,7 @@ use fix_codegen::{BytecodeContext, InstructionPtr}; use fix_common::{StringId, Symbol}; use fix_error::{Error, Result, Source}; use fix_ir::downgrade::{Downgrade as _, DowngradeContext}; -use fix_ir::{Ir, IrRef, RawIrRef, ThunkId}; +use fix_ir::{Ir, IrRef, MaybeThunk, RawIrRef, ThunkId}; use fix_vm::{ForceMode, StaticValue, Vm, VmContext}; use ghost_cell::{GhostCell, GhostToken}; use hashbrown::{HashMap, HashSet}; @@ -294,56 +294,66 @@ impl<'ctx: 'ir, 'id, 'ir> DowngradeContext<'id, 'ir> for DowngradeCtx<'ctx, 'id, IrRef::new(self.bump.alloc(GhostCell::new(expr))) } - fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> IrRef<'id, 'ir> { - if !should_thunk(ir, &self.token) { - return ir; + fn maybe_thunk(&mut self, ir: IrRef<'id, 'ir>) -> MaybeThunk { + use MaybeThunk::*; + match *ir.borrow(&self.token) { + Ir::Builtin(x) => return Builtin(x), + Ir::Int(x) => return Int(x), + Ir::Float(x) => return Float(x), + Ir::Bool(x) => return Bool(x), + Ir::Str(x) => return Str(x), + Ir::Thunk(x) => return Thunk(x), + Ir::Arg { layer } => return Arg { layer }, + Ir::Builtins => return Builtins, + Ir::Null => return Null, + _ => (), } - let id = ThunkId(*self.thunk_count); *self.thunk_count = self.thunk_count.checked_add(1).expect("thunk id overflow"); self.thunk_scopes .last_mut() .expect("no active cache scope") .add_binding(id, ir, &self.token); - IrRef::alloc(self.bump, Ir::Thunk(id)) + Thunk(id) } - fn new_sym(&mut self, sym: String) -> StringId { + fn intern_string(&mut self, sym: impl AsRef) -> StringId { StringId(self.strings.get_or_intern(sym)) } - fn get_sym(&self, id: StringId) -> Symbol<'_> { + fn resolve_sym(&self, id: StringId) -> Symbol<'_> { self.strings.resolve(id.0).expect("no symbol found").into() } - fn lookup(&self, sym: StringId, span: rnix::TextRange) -> Result> { + fn lookup(&self, sym: StringId, span: rnix::TextRange) -> Result { for scope in self.scopes.iter().rev() { match scope { &Scope::Global(global_scope) => { + use MaybeThunk::*; if let Some(expr) = global_scope.get(&sym) { - let ir = match expr { - Ir::Builtins => Ir::Builtins, - Ir::Builtin(s) => Ir::Builtin(*s), - Ir::Bool(b) => Ir::Bool(*b), - Ir::Null => Ir::Null, + let val = match expr { + Ir::Builtins => Builtins, + Ir::Builtin(s) => Builtin(*s), + Ir::Bool(b) => Bool(*b), + Ir::Null => Null, _ => unreachable!("globals should only contain leaf IR nodes"), }; - return Ok(self.new_expr(ir)); + return Ok(val); } } &Scope::Repl(repl_bindings) => { if repl_bindings.contains(&sym) { - return Ok(self.new_expr(Ir::ReplBinding(sym))); + return Ok(MaybeThunk::ReplBinding(sym)); } } Scope::ScopedImport(scoped_bindings) => { if scoped_bindings.contains(&sym) { - return Ok(self.new_expr(Ir::ScopedImportBinding(sym))); + return Ok(MaybeThunk::ScopedImportBinding(sym)); } } Scope::Let(let_scope) => { if let Some(&expr) = let_scope.get(&sym) { - return Ok(self.new_expr(Ir::Thunk(expr))); + return Ok(MaybeThunk::Thunk(expr)); } } &Scope::Param { @@ -351,19 +361,19 @@ impl<'ctx: 'ir, 'id, 'ir> DowngradeContext<'id, 'ir> for DowngradeCtx<'ctx, 'id, abs_layer, } => { if param_sym == sym { - return Ok(self.new_expr(Ir::Arg { + return Ok(MaybeThunk::Arg { layer: self.thunk_scopes.len() - abs_layer, - })); + }); } } } } if self.with_scope_count > 0 { - Ok(self.new_expr(Ir::WithLookup(sym))) + Ok(MaybeThunk::WithLookup(sym)) } else { Err(Error::downgrade_error( - format!("'{}' not found", self.get_sym(sym)), + format!("'{}' not found", self.resolve_sym(sym)), self.get_current_source(), span, ))