optimize: dedup consts

This commit is contained in:
2025-05-15 19:11:34 +08:00
parent 864be73e77
commit 1e50322af0
8 changed files with 51 additions and 31 deletions

View File

@@ -8,10 +8,10 @@ type Slice<T> = Box<[T]>;
pub type OpCodes = Slice<OpCode>; pub type OpCodes = Slice<OpCode>;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy)]
pub enum OpCode { pub enum OpCode {
/// load a constant onto stack /// load a constant onto stack
Const { value: Const }, Const { idx: usize },
/// load a dynamic var onto stack /// load a dynamic var onto stack
LookUp { sym: usize }, LookUp { sym: usize },
/// load a thunk lazily onto stack /// load a thunk lazily onto stack
@@ -114,4 +114,5 @@ pub struct Program {
pub funcs: Slice<Func>, pub funcs: Slice<Func>,
pub symbols: Vec<EcoString>, pub symbols: Vec<EcoString>,
pub symmap: HashMap<EcoString, usize>, pub symmap: HashMap<EcoString, usize>,
pub consts: Box<[Const]>
} }

View File

@@ -1,6 +1,5 @@
use crate::bytecode::*; use crate::bytecode::*;
use crate::ir; use crate::ir;
use crate::ty::internal::Const;
pub struct Compiler { pub struct Compiler {
opcodes: Vec<OpCode>, opcodes: Vec<OpCode>,
@@ -23,7 +22,8 @@ pub fn compile(downgraded: ir::Downgraded) -> Program {
}) })
.collect(), .collect(),
symbols: downgraded.symbols, symbols: downgraded.symbols,
symmap: downgraded.symmap symmap: downgraded.symmap,
consts: downgraded.consts
} }
} }
@@ -79,7 +79,7 @@ impl<T: Compile> CompileWithLength for T {
impl Compile for ir::Const { impl Compile for ir::Const {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::Const { value: self.value }); comp.push(OpCode::Const { idx: self.idx });
} }
} }

View File

@@ -112,7 +112,7 @@ ir! {
With => { namespace: Box<Ir>, expr: Box<Ir> }, With => { namespace: Box<Ir>, expr: Box<Ir> },
Assert => { assertion: Box<Ir>, expr: Box<Ir> }, Assert => { assertion: Box<Ir>, expr: Box<Ir> },
ConcatStrings => { parts: Vec<Ir> }, ConcatStrings => { parts: Vec<Ir> },
Const => { value: i::Const }, Const => { idx: usize },
Var => { sym: usize }, Var => { sym: usize },
#[derive(Copy)] #[derive(Copy)]
Thunk => { idx: usize }, Thunk => { idx: usize },
@@ -127,6 +127,7 @@ pub struct DowngradeContext {
thunks: Vec<Ir>, thunks: Vec<Ir>,
funcs: Vec<Func>, funcs: Vec<Func>,
consts: Vec<i::Const>, consts: Vec<i::Const>,
constmap: HashMap<i::Const, usize>,
symbols: Vec<EcoString>, symbols: Vec<EcoString>,
symmap: HashMap<EcoString, usize>, symmap: HashMap<EcoString, usize>,
} }
@@ -157,6 +158,16 @@ impl DowngradeContext {
LoadFunc { idx } LoadFunc { idx }
} }
fn new_const(&mut self, cnst: i::Const) -> Const {
if let Some(&idx) = self.constmap.get(&cnst) {
Const { idx }
} else {
self.constmap.insert(cnst.clone(), self.consts.len());
self.consts.push(cnst);
Const { idx: self.consts.len() - 1 }
}
}
fn new_sym(&mut self, sym: impl Into<EcoString>) -> usize { fn new_sym(&mut self, sym: impl Into<EcoString>) -> usize {
let sym = sym.into(); let sym = sym.into();
if let Some(&idx) = self.symmap.get(&sym) { if let Some(&idx) = self.symmap.get(&sym) {
@@ -414,9 +425,7 @@ impl Downgrade for ast::Path {
let parts = self let parts = self
.parts() .parts()
.map(|part| match part { .map(|part| match part {
ast::InterpolPart::Literal(lit) => Const { ast::InterpolPart::Literal(lit) => ctx.new_const(lit.to_string().into())
value: lit.to_string().into(),
}
.ir() .ir()
.ok(), .ok(),
ast::InterpolPart::Interpolation(interpol) => { ast::InterpolPart::Interpolation(interpol) => {
@@ -444,7 +453,7 @@ impl Downgrade for ast::Str {
.normalized_parts() .normalized_parts()
.into_iter() .into_iter()
.map(|part| match part { .map(|part| match part {
ast::InterpolPart::Literal(lit) => Const { value: lit.into() }.ir().ok(), ast::InterpolPart::Literal(lit) => ctx.new_const(lit.into()).ir().ok(),
ast::InterpolPart::Interpolation(interpol) => { ast::InterpolPart::Interpolation(interpol) => {
interpol.expr().unwrap().downgrade(ctx) interpol.expr().unwrap().downgrade(ctx)
} }
@@ -459,17 +468,11 @@ impl Downgrade for ast::Str {
} }
impl Downgrade for ast::Literal { impl Downgrade for ast::Literal {
fn downgrade(self, _ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
match self.kind() { match self.kind() {
ast::LiteralKind::Integer(int) => Const { ast::LiteralKind::Integer(int) => ctx.new_const(int.value().unwrap().into()),
value: int.value().unwrap().into(), ast::LiteralKind::Float(float) => ctx.new_const(float.value().unwrap().into()),
}, ast::LiteralKind::Uri(uri) => ctx.new_const(uri.to_string().into())
ast::LiteralKind::Float(float) => Const {
value: float.value().unwrap().into(),
},
ast::LiteralKind::Uri(uri) => Const {
value: uri.to_string().into(),
},
} }
.ir() .ir()
.ok() .ok()
@@ -519,11 +522,6 @@ impl Downgrade for ast::HasAttr {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
let attrs = self.expr().unwrap().downgrade(ctx)?; let attrs = self.expr().unwrap().downgrade(ctx)?;
let path = downgrade_attrpath(self.attrpath().unwrap(), ctx)?; let path = downgrade_attrpath(self.attrpath().unwrap(), ctx)?;
if let Some(attrs) = Downcast::<Attrs>::downcast_ref(&attrs) {
if let Some(res) = attrs.has_attr(&path) {
return Const { value: res.into() }.ir().ok();
}
}
HasAttr { HasAttr {
lhs: attrs.boxed(), lhs: attrs.boxed(),
rhs: path, rhs: path,
@@ -730,7 +728,7 @@ fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result<Attr> {
let parts = parts let parts = parts
.into_iter() .into_iter()
.map(|part| match part { .map(|part| match part {
Literal(lit) => Const { value: lit.into() }.ir().ok(), Literal(lit) => ctx.new_const(lit.into()).ir().ok(),
Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx), Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx),
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;

View File

@@ -1,3 +1,5 @@
use std::hash::Hash;
use derive_more::{IsVariant, Unwrap}; use derive_more::{IsVariant, Unwrap};
use ecow::EcoString; use ecow::EcoString;
@@ -7,6 +9,20 @@ pub enum Const {
Int(i64), Int(i64),
Float(f64), Float(f64),
String(EcoString), String(EcoString),
Null
}
impl Hash for Const {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use Const::*;
match self {
Int(x) => x.hash(state),
Float(x) => x.to_bits().hash(state),
Bool(x) => x.hash(state),
String(x) => x.hash(state),
x @ Null => x.hash(state),
}
}
} }
impl From<bool> for Const { impl From<bool> for Const {

View File

@@ -89,7 +89,7 @@ impl<'vm> Func<'vm> {
} }
} }
vm.eval(self.func.opcodes.clone(), env) vm.eval(self.func.opcodes.iter().copied(), env)
} }
} }

View File

@@ -458,7 +458,7 @@ impl<'vm> Thunk<'vm> {
_Thunk::SuspendedFrom(self as *const Thunk), _Thunk::SuspendedFrom(self as *const Thunk),
) )
.unwrap_code(); .unwrap_code();
let value = vm.eval(opcodes.clone(), env.get().unwrap().clone())?; let value = vm.eval(opcodes.iter().copied(), env.get().unwrap().clone())?;
let _ = std::mem::replace( let _ = std::mem::replace(
&mut *self.thunk.borrow_mut(), &mut *self.thunk.borrow_mut(),
_Thunk::Value(value.clone().into()), _Thunk::Value(value.clone().into()),

View File

@@ -13,6 +13,7 @@ pub enum Const {
Int(i64), Int(i64),
Float(f64), Float(f64),
String(EcoString), String(EcoString),
Null
} }
impl Display for Const { impl Display for Const {
@@ -23,6 +24,7 @@ impl Display for Const {
Int(i) => write!(f, "{i}"), Int(i) => write!(f, "{i}"),
Float(float) => write!(f, "{float}"), Float(float) => write!(f, "{float}"),
String(s) => write!(f, "{s}"), String(s) => write!(f, "{s}"),
Null => write!(f, "null")
} }
} }
} }
@@ -35,6 +37,7 @@ impl From<i::Const> for Const {
Int(int) => Const::Int(int), Int(int) => Const::Int(int),
Float(float) => Const::Float(float), Float(float) => Const::Float(float),
String(string) => Const::String(string), String(string) => Const::String(string),
Null => Const::Null
} }
} }
} }

View File

@@ -29,10 +29,11 @@ pub fn run(prog: Program, jit: JITContext<'_>) -> Result<p::Value> {
prog.funcs, prog.funcs,
RefCell::new(prog.symbols), RefCell::new(prog.symbols),
RefCell::new(prog.symmap), RefCell::new(prog.symmap),
prog.consts,
jit jit
); );
let env = env(&vm); let env = env(&vm);
let temp = vm.eval(prog.top_level, env)?; let temp = vm.eval(prog.top_level.into_iter(), env)?;
let temp = temp.to_public(&vm); let temp = temp.to_public(&vm);
Ok(temp) Ok(temp)
} }
@@ -43,6 +44,7 @@ pub struct VM<'jit> {
funcs: Box<[F]>, funcs: Box<[F]>,
symbols: RefCell<Vec<EcoString>>, symbols: RefCell<Vec<EcoString>>,
symmap: RefCell<HashMap<EcoString, usize>>, symmap: RefCell<HashMap<EcoString, usize>>,
consts: Box<[Const]>,
jit: JITContext<'jit>, jit: JITContext<'jit>,
} }
@@ -70,7 +72,7 @@ impl<'vm, 'jit: 'vm> VM<'jit> {
} }
} }
pub fn eval(&'vm self, opcodes: OpCodes, env: Rc<Env<'vm>>) -> Result<Value<'vm>> { pub fn eval(&'vm self, opcodes: impl Iterator<Item = OpCode>, env: Rc<Env<'vm>>) -> Result<Value<'vm>> {
let mut stack = Stack::<_, STACK_SIZE>::new(); let mut stack = Stack::<_, STACK_SIZE>::new();
let mut iter = opcodes.into_iter(); let mut iter = opcodes.into_iter();
while let Some(opcode) = iter.next() { while let Some(opcode) = iter.next() {
@@ -94,7 +96,7 @@ impl<'vm, 'jit: 'vm> VM<'jit> {
) -> Result<usize> { ) -> Result<usize> {
match opcode { match opcode {
OpCode::Illegal => panic!("illegal opcode"), OpCode::Illegal => panic!("illegal opcode"),
OpCode::Const { value } => stack.push(Value::Const(value))?, OpCode::Const { idx } => stack.push(Value::Const(self.consts[idx].clone()))?,
OpCode::LoadThunk { idx } => { OpCode::LoadThunk { idx } => {
stack.push(Value::Thunk(Thunk::new(self.get_thunk(idx))))? stack.push(Value::Thunk(Thunk::new(self.get_thunk(idx))))?
} }