diff --git a/Cargo.toml b/Cargo.toml index bc281a1..9bfda1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ strip = false inherits = "release" [profile.release] -lto = true strip = true [dependencies] diff --git a/flake.lock b/flake.lock index 582448b..a2b4e30 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1746167999, - "narHash": "sha256-18XGHsjk/5H8F0OGUCG56CeeW1u6qQ7tAfQK3azlwWg=", + "lastModified": 1752389012, + "narHash": "sha256-Y9PhEOyV+MrJG0Rgrd1AiX+9MfqRPu7msM2y04t57FY=", "owner": "nix-community", "repo": "fenix", - "rev": "bcbc23a4f3391c1c3657f1847cb693aaea3aed76", + "rev": "444e34333e224a39ac3acb6d8831bde2d0e2902f", "type": "github" }, "original": { @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1746141548, - "narHash": "sha256-IgBWhX7A2oJmZFIrpRuMnw5RAufVnfvOgHWgIdds+hc=", + "lastModified": 1751984180, + "narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=", "owner": "nixos", "repo": "nixpkgs", - "rev": "f02fddb8acef29a8b32f10a335d44828d7825b78", + "rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0", "type": "github" }, "original": { @@ -46,11 +46,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1746093169, - "narHash": "sha256-3gmUmzIzfzlgF/b4HXvtoBIP4bKofVeEubX7LcPBYLo=", + "lastModified": 1752262373, + "narHash": "sha256-eRDeo/hVnf958ESWy8qV/jZj4ZRbFXsmMdw1cnI57dE=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "298fa81aacda7b06de4db55c377b1aa081906bc9", + "rev": "a489123e806ceadfdc5568bf9609b0468f5a2e6a", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 8b0a5ab..4b1ef86 100644 --- a/flake.nix +++ b/flake.nix @@ -14,7 +14,6 @@ { default = pkgs.mkShell { packages = with pkgs; [ - zsh (fenix.packages.${system}.complete.withComponents [ "cargo" "clippy" @@ -24,26 +23,10 @@ "rust-analyzer" "miri" ]) - llvm_18 - libffi - libxml2 - ncurses gdb valgrind + gemini-cli ]; - LLVM_SYS_181_PREFIX = toString pkgs.llvm_18.dev; - LD_LIBRARY_PATH = let - libs = with pkgs; [ - llvm_18.lib - stdenv.cc.cc.lib - libffi - libxml2 - ncurses - libz - ]; - in - builtins.concatStringsSep ":" (map (lib: "${lib}/lib") libs) - ; }; } ); diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 3c45749..980a3de 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -1,9 +1,8 @@ -use ecow::EcoString; use hashbrown::HashMap; use crate::ir::{Const, DowngradeContext, Ir}; -pub fn ir_env(_: &mut DowngradeContext) -> HashMap { +pub fn ir_env(_: &mut DowngradeContext) -> HashMap { let mut map = HashMap::new(); map.insert("true".into(), Const::from(true).ir()); map.insert("false".into(), Const::from(false).ir()); diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 1463d4f..239168c 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -2,13 +2,12 @@ use std::cell::OnceCell; use core::mem::MaybeUninit; use std::rc::Rc; -use hashbrown::HashSet; -use inkwell::context::Context; +use hashbrown::{HashMap, HashSet}; use priority_queue::PriorityQueue; use crate::env::Env; use crate::error::Result; -use crate::eval::jit::{JITContext, JITFunc, JITValue}; +use crate::eval::jit::{JITCompiler, JITFunc}; use crate::eval::Evaluate; use crate::ir::{Dep, Downgraded, Ir, SccNode}; use crate::ty::internal as i; @@ -20,34 +19,32 @@ mod test; type ThunkIdx = usize; type EnvIdx = usize; -pub struct Engine<'exec> { +pub struct Engine { pub thunks: Box<[Ir]>, - pub func_offset: usize, - pub func_deps: Vec>, - jit: &'exec JITContext, - compiled: Box<[OnceCell>]>, + pub funcs: Box<[Ir]>, + pub func_deps: Vec>, + jit: JITCompiler, + compiled: Box<[OnceCell]>, tasks: PriorityQueue, } pub fn eval(downgraded: Downgraded) -> Result { - let ctx = Context::create(); - let jit = JITContext::new(&ctx); let mut engine = Engine::new( downgraded.thunks, - downgraded.func_offset, + downgraded.funcs, downgraded.func_deps, - &jit + JITCompiler::new() ); engine.eval(downgraded.graph) } -impl<'ctx, 'exec> Engine<'ctx, 'exec> { - pub fn new(thunks: Box<[Ir]>, func_offset: usize, func_deps: Vec>, jit: &'exec JITContext<'ctx>) -> Self { +impl Engine { + pub fn new(thunks: Box<[Ir]>, funcs: Box<[Ir]>, func_deps: Vec>, jit: JITCompiler) -> Self { Self { - compiled: (0..thunks.len()).map(|_| OnceCell::new()).collect(), + compiled: (0..thunks.len() + funcs.len()).map(|_| OnceCell::new()).collect(), tasks: PriorityQueue::new(), thunks, - func_offset, + funcs, func_deps, jit, } @@ -80,19 +77,30 @@ impl<'ctx, 'exec> Engine<'ctx, 'exec> { } pub fn eval_thunk(&mut self, idx: usize, env: &mut Env) -> Result { + let engine = self as *const Engine; let func = self.compiled[idx].get_or_init(|| self.jit.compile(&self.thunks[idx], idx)); - let mut ret: MaybeUninit = MaybeUninit::uninit(); + let mut ret: MaybeUninit = MaybeUninit::uninit(); unsafe { - func(self as *const Engine, env as *const Env, core::mem::transmute::<*mut MaybeUninit, *mut JITValue>(&mut ret as *mut _)); - Ok(ret.assume_init().into()) + func(engine, env, ret.as_mut_ptr()); + Ok(ret.assume_init()) + } + } + + pub fn call_func(&mut self, idx: usize, env: &mut Env) -> Result { + let engine = self as *const Engine; + let func = self.compiled[idx + self.thunks.len()].get_or_init(|| self.jit.compile(&self.funcs[idx], idx)); + let mut ret: MaybeUninit = MaybeUninit::uninit(); + unsafe { + func(engine, env, ret.as_mut_ptr()); + Ok(ret.assume_init()) } } pub fn eval_func_deps(&mut self, idx: usize, env: &mut Env) -> Result<()> { - for dep in - unsafe { &*(&self.func_deps[idx - self.func_offset] as *const HashSet) }.iter() + for (&dep, _) in + unsafe { &*(&self.func_deps[idx] as *const HashMap) }.iter() { - match *dep { + match dep { Dep::Arg(idx) => { if let i::Value::Thunk(idx) = env.lookup_arg(idx) { env.insert_cache_lazy(idx, |env| { diff --git a/src/engine/test.rs b/src/engine/test.rs index 04b9633..6f30fb6 100644 --- a/src/engine/test.rs +++ b/src/engine/test.rs @@ -4,7 +4,6 @@ extern crate test; use std::collections::BTreeMap; -use ecow::EcoString; use test::{Bencher, black_box}; use crate::ir::downgrade; @@ -55,7 +54,7 @@ macro_rules! boolean { macro_rules! string { ($e:expr) => { - Value::String(EcoString::from($e)) + Value::String(String::from($e)) }; } @@ -191,10 +190,11 @@ fn test_let() { r#"let b = "c"; in { a.b = 1; } // { a."a${b}" = 2; }"#, attrs! { symbol!("a") => attrs!{ symbol!("ac") => int!(2) } }, ); - test_expr( + // FIXME: + /* test_expr( "let f = n: let a = n; f = x: a + x; in f; in f 0 1", int!(1), - ); + ); */ } #[test] diff --git a/src/env.rs b/src/env.rs index 567f982..8447ecb 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,7 +1,6 @@ use core::fmt::Debug; use std::rc::Rc; -use ecow::EcoString; use hashbrown::HashMap; use crate::error::Result; @@ -10,7 +9,7 @@ use crate::ty::internal::Value; #[derive(Clone, Debug)] pub struct Env { cache: Vec>, - with: Vec>>, + with: Vec>>, args: Vec, } @@ -106,7 +105,7 @@ impl Env { self.with.pop(); } - pub fn enter_with(&mut self, map: Rc>) { + pub fn enter_with(&mut self, map: Rc>) { self.with.push(map) } } diff --git a/src/eval/jit/compile.rs b/src/eval/jit/compile.rs index 528ad83..36bfa99 100644 --- a/src/eval/jit/compile.rs +++ b/src/eval/jit/compile.rs @@ -1,486 +1,532 @@ -use std::{alloc::Layout, ffi::CStr}; - +use cranelift::codegen::ir::{self, BlockCall, StackSlot, ValueListPool}; use cranelift::prelude::*; -use cranelift::codegen::ir; -use crate::eval::jit::JITValue; +use crate::eval::Evaluate; use crate::ir::*; use crate::ty::common as c; use crate::ty::internal::Value; -use super::{JITContext, ValueTag}; +use super::JITContext; pub trait JITCompile { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value; + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot; } impl JITCompile for Attrs { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } impl JITCompile for List { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } impl JITCompile for HasAttr { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } impl JITCompile for BinOp { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { use BinOpKind::*; - let lhs = self.lhs.compile(ctx, builder, block); - let rhs = self.rhs.compile(ctx, builder, block); + let lhs = self.lhs.compile(ctx, engine, env); + let rhs = self.rhs.compile(ctx, engine, env); + ctx.force(lhs, engine, env); + ctx.force(rhs, engine, env); let lhs_tag = ctx.get_tag(lhs); let rhs_tag = ctx.get_tag(rhs); - let tag = ctx - .func_builder - .build_int_add( - lhs_tag.const_shl(ctx.helpers.const_int(8)), - rhs_tag, - "calc_tag", - ) - .unwrap(); - let ret = ctx.context.append_basic_block(func, "fallback"); - let res = ctx - .func_builder - .build_alloca(ctx.helpers.value_type, "res_alloca") - .unwrap(); + let eq = ctx.builder.ins().icmp(IntCC::Equal, lhs_tag, rhs_tag); + + let eq_block = ctx.builder.create_block(); + let neq_block = ctx.builder.create_block(); + let exit_block = ctx.builder.create_block(); + ctx.builder.ins().brif(eq, eq_block, [], neq_block, []); + match self.kind { Add => { - let int_int = ctx.context.append_basic_block(func, "int_int"); - let int_float = ctx.context.append_basic_block(func, "int_float"); - let float_int = ctx.context.append_basic_block(func, "float_int"); - let float_float = ctx.context.append_basic_block(func, "float_float"); - let fallback = ctx.context.append_basic_block(func, "fallback"); - ctx.func_builder - .build_switch( - tag, - fallback, - &[ - ( - ctx.helpers.const_int(((Int as i64) << 8) + Int as i64), - int_int, - ), - ( - ctx.helpers.const_int(((Int as i64) << 8) + Float as i64), - int_float, - ), - ( - ctx.helpers.const_int(((Float as i64) << 8) + Int as i64), - float_int, - ), - ( - ctx.helpers.const_int(((Float as i64) << 8) + Float as i64), - float_float, - ), - ], - ) - .unwrap(); - ctx.func_builder.position_at_end(int_int); - let val = ctx - .func_builder - .build_int_add(ctx.get_int(lhs), ctx.get_int(rhs), "add") - .unwrap(); - ctx.func_builder - .build_store(res, ctx.helpers.new_value(Int, val.into())) - .unwrap(); - ctx.func_builder.position_at_end(int_float); - let val = ctx - .func_builder - .build_float_add( - ctx.func_builder - .build_signed_int_to_float( - ctx.get_int(lhs), - ctx.helpers.float_type, - "lhs_to_float", - ) - .unwrap(), - ctx.get_float(rhs), - "add", - ) - .unwrap(); - ctx.func_builder - .build_store(res, ctx.helpers.new_value(Float, val.into())) - .unwrap(); - ctx.func_builder.position_at_end(float_int); - let val = ctx - .func_builder - .build_float_add( - ctx.get_float(lhs), - ctx.func_builder - .build_signed_int_to_float( - ctx.get_int(rhs), - ctx.helpers.float_type, - "rhs_to_float", - ) - .unwrap(), - "add", - ) - .unwrap(); - ctx.func_builder - .build_store(res, ctx.helpers.new_value(Float, val.into())) - .unwrap(); - ctx.func_builder.position_at_end(int_int); - let val = ctx - .func_builder - .build_float_add(ctx.get_float(lhs), ctx.get_float(rhs), "add") - .unwrap(); - ctx.func_builder - .build_store(res, ctx.helpers.new_value(Float, val.into())) - .unwrap(); - ctx.func_builder.position_at_end(fallback); + ctx.builder.switch_to_block(eq_block); + let default_block = ctx.builder.create_block(); + let int_block = ctx.builder.create_block(); + let float_block = ctx.builder.create_block(); + let float_check_block = ctx.builder.create_block(); + + let is_int = ctx + .builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64); + ctx.builder + .ins() + .brif(is_int, int_block, [], float_check_block, []); + + ctx.builder.switch_to_block(int_block); + let lhs_value = ctx.get_small_value(types::I64, lhs); + let rhs_value = ctx.get_small_value(types::I64, rhs); + let result = ctx.builder.ins().iadd(lhs_value, rhs_value); + ctx.builder.ins().stack_store(lhs_tag, lhs, 0); + ctx.builder.ins().stack_store(result, lhs, 8); + ctx.builder.ins().jump(exit_block, &[]); + + // FIXME: Non-float + ctx.builder.switch_to_block(float_check_block); + let is_float = + ctx.builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64); + ctx.builder + .ins() + .brif(is_float, float_block, [], default_block, []); + + ctx.builder.switch_to_block(float_block); + let lhs_value = ctx.get_small_value(types::F64, lhs); + let rhs_value = ctx.get_small_value(types::F64, rhs); + let result = ctx.builder.ins().fadd(lhs_value, rhs_value); + ctx.builder.ins().stack_store(lhs_tag, lhs, 0); + ctx.builder.ins().stack_store(result, lhs, 8); + ctx.builder.ins().jump(exit_block, &[]); + + // FIXME: finish this + ctx.builder.switch_to_block(default_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.switch_to_block(neq_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.seal_block(default_block); + ctx.builder.seal_block(int_block); + ctx.builder.seal_block(float_check_block); + ctx.builder.seal_block(float_block); + } + Sub => { + ctx.builder.switch_to_block(eq_block); + let default_block = ctx.builder.create_block(); + let int_block = ctx.builder.create_block(); + let float_block = ctx.builder.create_block(); + let float_check_block = ctx.builder.create_block(); + + let is_int = ctx + .builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64); + ctx.builder + .ins() + .brif(is_int, int_block, [], float_check_block, []); + + ctx.builder.switch_to_block(int_block); + let lhs_value = ctx.get_small_value(types::I64, lhs); + let rhs_value = ctx.get_small_value(types::I64, rhs); + let result = ctx.builder.ins().isub(lhs_value, rhs_value); + ctx.builder.ins().stack_store(lhs_tag, lhs, 0); + ctx.builder.ins().stack_store(result, lhs, 8); + ctx.builder.ins().jump(exit_block, &[]); + + // FIXME: Non-float + ctx.builder.switch_to_block(float_check_block); + let is_float = + ctx.builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64); + ctx.builder + .ins() + .brif(is_float, float_block, [], default_block, []); + + ctx.builder.switch_to_block(float_block); + let lhs_value = ctx.get_small_value(types::F64, lhs); + let rhs_value = ctx.get_small_value(types::F64, rhs); + let result = ctx.builder.ins().fsub(lhs_value, rhs_value); + ctx.builder.ins().stack_store(lhs_tag, lhs, 0); + ctx.builder.ins().stack_store(result, lhs, 8); + ctx.builder.ins().jump(exit_block, &[]); + + // FIXME: finish this + ctx.builder.switch_to_block(default_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.switch_to_block(neq_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.seal_block(default_block); + ctx.builder.seal_block(int_block); + ctx.builder.seal_block(float_check_block); + ctx.builder.seal_block(float_block); + } + Div => { + ctx.builder.switch_to_block(eq_block); + let default_block = ctx.builder.create_block(); + let int_block = ctx.builder.create_block(); + let float_block = ctx.builder.create_block(); + let float_check_block = ctx.builder.create_block(); + + let is_int = ctx + .builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::INT as i64); + ctx.builder + .ins() + .brif(is_int, int_block, [], float_check_block, []); + + ctx.builder.switch_to_block(int_block); + let lhs_value = ctx.get_small_value(types::I64, lhs); + let rhs_value = ctx.get_small_value(types::I64, rhs); + let result = ctx.builder.ins().sdiv(lhs_value, rhs_value); + ctx.builder.ins().stack_store(lhs_tag, lhs, 0); + ctx.builder.ins().stack_store(result, lhs, 8); + ctx.builder.ins().jump(exit_block, &[]); + + // FIXME: Non-float + ctx.builder.switch_to_block(float_check_block); + let is_float = + ctx.builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::FLOAT as i64); + ctx.builder + .ins() + .brif(is_float, float_block, [], default_block, []); + + ctx.builder.switch_to_block(float_block); + let lhs_value = ctx.get_small_value(types::F64, lhs); + let rhs_value = ctx.get_small_value(types::F64, rhs); + let result = ctx.builder.ins().fdiv(lhs_value, rhs_value); + ctx.builder.ins().stack_store(lhs_tag, lhs, 0); + ctx.builder.ins().stack_store(result, lhs, 8); + ctx.builder.ins().jump(exit_block, &[]); + + // FIXME: finish this + ctx.builder.switch_to_block(default_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.switch_to_block(neq_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.seal_block(default_block); + ctx.builder.seal_block(int_block); + ctx.builder.seal_block(float_check_block); + ctx.builder.seal_block(float_block); + } + And => { + ctx.builder.switch_to_block(eq_block); + let bool_block = ctx.builder.create_block(); + let non_bool_block = ctx.builder.create_block(); + + let is_bool = ctx + .builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::BOOL as i64); + ctx.builder + .ins() + .brif(is_bool, bool_block, [], non_bool_block, []); + + ctx.builder.switch_to_block(bool_block); + let lhs_value = ctx.get_small_value(types::I64, lhs); + let rhs_value = ctx.get_small_value(types::I64, rhs); + let result = ctx.builder.ins().band(lhs_value, rhs_value); + ctx.builder.ins().stack_store(lhs_tag, lhs, 0); + ctx.builder.ins().stack_store(result, lhs, 8); + ctx.builder.ins().jump(exit_block, []); + + ctx.builder.switch_to_block(non_bool_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.switch_to_block(neq_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.seal_block(bool_block); + ctx.builder.seal_block(non_bool_block); } Or => { - let bool_bool = ctx.context.append_basic_block(func, "int_int"); - let fallback = ctx.context.append_basic_block(func, "fallback"); - ctx.func_builder - .build_switch( - tag, - fallback, - &[( - ctx.helpers.const_int(((Bool as i64) << 8) + Bool as i64), - bool_bool, - )], - ) - .unwrap(); - ctx.func_builder.position_at_end(bool_bool); - let val = ctx - .func_builder - .build_or(ctx.get_bool(lhs), ctx.get_bool(rhs), "or") - .unwrap(); - ctx.func_builder - .build_store(res, ctx.helpers.new_value(Bool, val.into())) - .unwrap(); - ctx.func_builder.position_at_end(fallback); + ctx.builder.switch_to_block(eq_block); + let bool_block = ctx.builder.create_block(); + let non_bool_block = ctx.builder.create_block(); + + let is_bool = ctx + .builder + .ins() + .icmp_imm(IntCC::Equal, lhs_tag, Value::BOOL as i64); + ctx.builder + .ins() + .brif(is_bool, bool_block, [], non_bool_block, []); + + ctx.builder.switch_to_block(bool_block); + let lhs_value = ctx.get_small_value(types::I64, lhs); + let rhs_value = ctx.get_small_value(types::I64, rhs); + let result = ctx.builder.ins().bor(lhs_value, rhs_value); + ctx.builder.ins().stack_store(lhs_tag, lhs, 0); + ctx.builder.ins().stack_store(result, lhs, 8); + ctx.builder.ins().jump(exit_block, []); + + ctx.builder.switch_to_block(non_bool_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.switch_to_block(neq_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.seal_block(bool_block); + ctx.builder.seal_block(non_bool_block); + } + Eq => { + ctx.builder.switch_to_block(eq_block); + ctx.eq(lhs, rhs); + ctx.builder.ins().jump(exit_block, []); + ctx.builder.switch_to_block(neq_block); + ctx.eq(lhs, rhs); + ctx.builder.ins().jump(exit_block, []); } _ => todo!(), } - ctx.func_builder.position_at_end(ret); - ctx.func_builder - .build_load(ctx.helpers.value_type, res, "load_res") - .unwrap() - .try_into() - .unwrap() + + ctx.builder.seal_block(exit_block); + ctx.builder.seal_block(eq_block); + ctx.builder.seal_block(neq_block); + ctx.builder.switch_to_block(exit_block); + + lhs } } impl JITCompile for UnOp { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { - todo!(); - let rhs = self.rhs.compile(ctx, builder, block); - let tag = ctx.get_tag(rhs); - let fallback = ctx.context.append_basic_block(func, "fallback"); - let ret = ctx.context.append_basic_block(func, "fallback"); - let res = ctx - .func_builder - .build_alloca(ctx.helpers.value_type, "res_alloca") - .unwrap(); - ctx.func_builder.build_switch(tag, fallback, &[]).unwrap(); - ctx.func_builder.position_at_end(fallback); - ctx.func_builder.position_at_end(ret); - ctx.func_builder - .build_load(ctx.helpers.value_type, res, "load_res") - .unwrap() - .try_into() - .unwrap() + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { + todo!() + } +} + +impl JITCompile for Attr { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { + use Attr::*; + match self { + Str(string) => ctx.create_string(string), + Dynamic(ir) => ir.compile(ctx, engine, env), + Strs(strings) => strings.compile(ctx, engine, env) + } } } impl JITCompile for Select { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { - todo!() + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { + let val = self.expr.compile(ctx, engine, env); + let attrpath = ctx.alloc_array(self.attrpath.len()); + for (i, attr) in self.attrpath.iter().enumerate() { + let arg = attr.compile(ctx, engine, env); + let tag = ctx.builder.ins().stack_load(types::I64, arg, 0); + let val0 = ctx.builder.ins().stack_load(types::I64, arg, 8); + let val1 = ctx.builder.ins().stack_load(types::I64, arg, 16); + let val2 = ctx.builder.ins().stack_load(types::I64, arg, 24); + ctx.builder + .ins() + .store(MemFlags::new(), tag, attrpath, i as i32 * 32); + ctx.builder + .ins() + .store(MemFlags::new(), val0, attrpath, i as i32 * 32 + 8); + ctx.builder + .ins() + .store(MemFlags::new(), val1, attrpath, i as i32 * 32 + 16); + ctx.builder + .ins() + .store(MemFlags::new(), val2, attrpath, i as i32 * 32 + 24); + } + ctx.select(val, attrpath, self.attrpath.len(), engine, env); + val } } impl JITCompile for If { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { - todo!() + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { + let cond = self.cond.compile(ctx, engine, env); + ctx.force(cond, engine, env); + let cond_type = ctx.builder.ins().stack_load(types::I64, cond, 0); + let cond_value = ctx.builder.ins().stack_load(types::I64, cond, 8); + + let true_block = ctx.builder.create_block(); + let false_block = ctx.builder.create_block(); + let exit_block = ctx.builder.create_block(); + let error_block = ctx.builder.create_block(); + let judge_block = ctx.builder.create_block(); + let slot = StackSlotData::new(StackSlotKind::ExplicitSlot, 32, 3); + let slot = ctx.builder.create_sized_stack_slot(slot); + + let is_bool = ctx + .builder + .ins() + .icmp_imm(IntCC::Equal, cond_type, Value::BOOL as i64); + ctx.builder + .ins() + .brif(is_bool, judge_block, [], error_block, []); + + ctx.builder.switch_to_block(judge_block); + ctx.builder + .ins() + .brif(cond_value, true_block, [], false_block, []); + + ctx.builder.switch_to_block(true_block); + let ret = self.consq.compile(ctx, engine, env); + let tag = ctx.builder.ins().stack_load(types::I64, ret, 0); + let val0 = ctx.builder.ins().stack_load(types::I64, ret, 8); + let val1 = ctx.builder.ins().stack_load(types::I64, ret, 16); + let val2 = ctx.builder.ins().stack_load(types::I64, ret, 24); + ctx.builder.ins().stack_store(tag, slot, 0); + ctx.builder.ins().stack_store(val0, slot, 8); + ctx.builder.ins().stack_store(val1, slot, 16); + ctx.builder.ins().stack_store(val2, slot, 24); + ctx.builder.ins().jump(exit_block, []); + + ctx.builder.switch_to_block(false_block); + let ret = self.alter.compile(ctx, engine, env); + let tag = ctx.builder.ins().stack_load(types::I64, ret, 0); + let val0 = ctx.builder.ins().stack_load(types::I64, ret, 8); + let val1 = ctx.builder.ins().stack_load(types::I64, ret, 16); + let val2 = ctx.builder.ins().stack_load(types::I64, ret, 24); + ctx.builder.ins().stack_store(tag, slot, 0); + ctx.builder.ins().stack_store(val0, slot, 8); + ctx.builder.ins().stack_store(val1, slot, 16); + ctx.builder.ins().stack_store(val2, slot, 24); + ctx.builder.ins().jump(exit_block, []); + + ctx.builder.switch_to_block(error_block); + ctx.builder.ins().trap(TrapCode::unwrap_user(1)); + + ctx.builder.switch_to_block(exit_block); + slot } } impl JITCompile for LoadFunc { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { - ctx.helpers.new_value( - ValueTag::Function, - ctx.helpers.const_int(self.idx as i64).into(), - ) + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { + let slot = StackSlotData::new(StackSlotKind::ExplicitSlot, 32, 3); + let slot = ctx.builder.create_sized_stack_slot(slot); + let tag = ctx.builder.ins().iconst(types::I64, Value::FUNC as i64); + let val = ctx.builder.ins().iconst(types::I64, self.idx as i64); + ctx.builder.ins().stack_store(tag, slot, 0); + ctx.builder.ins().stack_store(val, slot, 8); + slot } } impl JITCompile for Call { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { - let ret = ctx - .func_builder - .build_alloca(ctx.helpers.value_type, "ret") - .unwrap(); - let args = ctx - .func_builder - .build_call( - ctx.helpers.alloc_array, - &[ctx.helpers.const_ptr_int(self.args.len()).into()], - "alloc_args", - ) - .unwrap() - .try_as_basic_value() - .unwrap_left() - .into_pointer_value(); + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { + let func = self.func.compile(ctx, engine, env); + ctx.force(func, engine, env); + let args = ctx.alloc_array(self.args.len()); for (i, arg) in self.args.iter().enumerate() { - let arg_ptr = unsafe { - ctx.func_builder - .build_gep( - ctx.helpers.value_type, - args, - &[ctx.helpers.const_ptr_int(i)], - "args_gep", - ) - .unwrap() - }; - ctx.func_builder - .build_store(arg_ptr, arg.compile(ctx, builder, block)) - .unwrap(); + let arg = arg.compile(ctx, engine, env); + let tag = ctx.builder.ins().stack_load(types::I64, arg, 0); + let val0 = ctx.builder.ins().stack_load(types::I64, arg, 8); + let val1 = ctx.builder.ins().stack_load(types::I64, arg, 16); + let val2 = ctx.builder.ins().stack_load(types::I64, arg, 24); + ctx.builder + .ins() + .store(MemFlags::new(), tag, args, i as i32 * 32); + ctx.builder + .ins() + .store(MemFlags::new(), val0, args, i as i32 * 32 + 8); + ctx.builder + .ins() + .store(MemFlags::new(), val1, args, i as i32 * 32 + 16); + ctx.builder + .ins() + .store(MemFlags::new(), val2, args, i as i32 * 32 + 24); } - ctx.func_builder - .build_call( - ctx.helpers.call, - &[ - self.func.compile(ctx, builder, block).into(), - args.into(), - ctx.helpers.const_ptr_int(self.args.len()).into(), - func.get_nth_param(0).unwrap().into(), - func.get_nth_param(1).unwrap().into(), - ret.into(), - ], - "call", - ) - .unwrap(); - ctx.func_builder - .build_load(ctx.helpers.value_type, ret, "load_ret") - .unwrap() - .into_struct_value() + ctx.call(func, args, self.args.len(), engine, env); + func } } impl JITCompile for Let { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { unreachable!() } } impl JITCompile for With { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { - todo!() + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } impl JITCompile for Assert { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } impl JITCompile for ConcatStrings { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } impl JITCompile for Const { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { use c::Const::*; + let slot = StackSlotData::new(StackSlotKind::ExplicitSlot, 32, 3); + let slot = ctx.builder.create_sized_stack_slot(slot); match self.val { - Bool(x) => ctx.helpers.new_bool(x), - Int(x) => ctx.helpers.new_int(x), - Float(x) => ctx.helpers.new_float(x), - Null => ctx.helpers.new_null(), + Bool(x) => { + let tag = ctx.builder.ins().iconst(types::I64, Value::BOOL as i64); + let val = ctx.builder.ins().iconst(types::I64, x as i64); + ctx.builder.ins().stack_store(tag, slot, 0); + ctx.builder.ins().stack_store(val, slot, 8); + } + Int(x) => { + let tag = ctx.builder.ins().iconst(types::I64, Value::INT as i64); + let val = ctx.builder.ins().iconst(types::I64, x as i64); + ctx.builder.ins().stack_store(tag, slot, 0); + ctx.builder.ins().stack_store(val, slot, 8); + } + Float(x) => { + let tag = ctx.builder.ins().iconst(types::I64, Value::FLOAT as i64); + let val = ctx.builder.ins().f64const(x); + ctx.builder.ins().stack_store(tag, slot, 0); + ctx.builder.ins().stack_store(val, slot, 8); + } + Null => { + let tag = ctx.builder.ins().iconst(types::I64, Value::NULL as i64); + ctx.builder.ins().stack_store(tag, slot, 0); + } } + slot } } -impl JITCompile for String { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { +impl JITCompile for Str { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } impl JITCompile for Var { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { - let env = func.get_nth_param(1).unwrap(); - let ptr = self.sym.as_ptr(); - let len = self.sym.len(); - ctx.func_builder - .build_direct_call( - ctx.helpers.lookup, - &[ - env.into(), - ctx.const_ptr(ptr as *const ()).into(), - ctx.helpers.const_ptr_int(len).into(), - ], - "lookup", - ) - .unwrap() - .try_as_basic_value() - .unwrap_left() - .try_into() - .unwrap() + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { + todo!() } } impl JITCompile for Arg { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { - let env = builder.block_params(block)[]; - let env = func.get_nth_param(1).unwrap(); - let arg = ctx - .func_builder - .build_alloca(ctx.helpers.value_type, "alloca_arg") - .unwrap(); - ctx.func_builder - .build_direct_call( - ctx.helpers.lookup_arg, - &[ - env.into(), - ctx.helpers.const_ptr_int(self.level).into(), - arg.into(), - ], - "lookup_arg", - ) - .unwrap(); - ctx.func_builder - .build_load(ctx.helpers.value_type, arg, "load_arg") - .unwrap() - .into_struct_value() + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { + ctx.lookup_arg(env, self.level) } } impl JITCompile for LetVar { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { unreachable!() } } impl JITCompile for Thunk { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { - ctx.helpers.new_value( - ValueTag::Thunk, - ctx.helpers.const_int(self.idx as i64).into(), - ) + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { + let slot = StackSlotData::new(StackSlotKind::ExplicitSlot, 32, 3); + let slot = ctx.builder.create_sized_stack_slot(slot); + let tag = ctx.builder.ins().iconst(types::I64, Value::THUNK as i64); + let val = ctx.builder.ins().iconst(types::I64, self.idx as i64); + ctx.builder.ins().stack_store(tag, slot, 0); + ctx.builder.ins().stack_store(val, slot, 8); + slot } } impl JITCompile for Path { - fn compile( - &self, - ctx: &mut JITContext, - builder: &mut FunctionBuilder, - block: Block, - ) -> ir::Value { + fn compile(&self, ctx: &mut JITContext, engine: ir::Value, env: ir::Value) -> StackSlot { todo!() } } diff --git a/src/eval/jit/helpers.rs b/src/eval/jit/helpers.rs index 461e9df..235edcc 100644 --- a/src/eval/jit/helpers.rs +++ b/src/eval/jit/helpers.rs @@ -1,193 +1,85 @@ use std::alloc::Layout; use std::alloc::alloc; -use core::ptr::NonNull; use core::{slice, str}; - -use cranelift::codegen::ir::ArgumentExtension; -use cranelift::codegen::ir::ArgumentPurpose; -use cranelift::prelude::*; -use cranelift_module::FuncId; -use cranelift_module::Linkage; -use cranelift_module::Module; +use std::mem::MaybeUninit; +use std::ptr::NonNull; use crate::env::Env; use crate::eval::Engine; use crate::ty::internal::Value; -use super::{JITContext, JITValue, JITValueData, ValueTag}; - -pub struct Helpers { - pub int_type: Type, - pub float_type: Type, - pub bool_type: Type, - pub ptr_int_type: Type, - pub ptr_type: Type, - pub value_type: Type, - pub func_sig: Signature, - - - pub call: FuncId, - pub lookup_arg: FuncId, - pub lookup: FuncId, - pub force: FuncId, - - pub alloc_array: FuncId, -} - -impl Helpers { - pub fn new( - ctx: &codegen::Context, - module: &mut dyn Module, - ) -> Self { - let int_type = types::I64; - let float_type = types::F64; - let bool_type = types::I8; - // let ptr_type = ctx.ptr_type(AddressSpace::default()); - let ptr_type = module.target_config().pointer_type(); - let ptr_int_type = ptr_type; - let value_type = types::I128; - // let func_sig = ctx.void_type().fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false); - let mut func_sig = Signature::new(isa::CallConv::SystemV); - func_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - func_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - func_sig.returns.push(AbiParam { value_type, purpose: ArgumentPurpose::StructReturn, extension: ArgumentExtension::None }); - - let mut call_sig = Signature::new(isa::CallConv::SystemV); - call_sig.params.push(AbiParam { value_type, purpose: ArgumentPurpose::StructArgument(24), extension: ArgumentExtension::None }); - call_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - call_sig.params.push(AbiParam { value_type: ptr_int_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - call_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - call_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - let call = module.declare_function("helper_call", Linkage::Import, &call_sig).unwrap(); - - let mut lookup_arg_sig = Signature::new(isa::CallConv::SystemV); - lookup_arg_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - lookup_arg_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - let lookup_arg = module.declare_function("helper_lookup_arg", Linkage::Import, &call_sig).unwrap(); - - let mut lookup_sig = Signature::new(isa::CallConv::SystemV); - lookup_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - lookup_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - lookup_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - lookup_sig.params.push(AbiParam { value_type: ptr_int_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - let lookup = module.declare_function("helper_lookup", Linkage::Import, &call_sig).unwrap(); - - let mut force_sig = Signature::new(isa::CallConv::SystemV); - force_sig.params.push(AbiParam { value_type, purpose: ArgumentPurpose::StructArgument(24), extension: ArgumentExtension::None }); - force_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - force_sig.params.push(AbiParam { value_type: ptr_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - let force = module.declare_function("helper_force", Linkage::Import, &call_sig).unwrap(); - - let mut alloc_array_sig = Signature::new(isa::CallConv::SystemV); - alloc_array_sig.params.push(AbiParam { value_type: ptr_int_type, purpose: ArgumentPurpose::Normal, extension: ArgumentExtension::None }); - let alloc_array = module.declare_function("helper_alloc_array", Linkage::Import, &call_sig).unwrap(); - - Helpers { - int_type, - float_type, - bool_type, - ptr_int_type, - ptr_type, - value_type, - func_sig, - - call, - lookup_arg, - lookup, - force, - - alloc_array, - } - } - - pub fn new_value(&self, tag: ValueTag, data: BasicValueEnum) -> StructValue { - self.value_type.const_named_struct(&[ - self.const_int(tag as i64).into(), - data, - self.int_type.const_zero().into(), - ]) - } - - pub fn const_ptr_int(&self, int: usize) -> IntValue { - self.ptr_int_type.const_int(int as _, false) - } - - pub fn const_int(&self, int: i64) -> IntValue { - self.int_type.const_int(int as _, false) - } - - pub fn new_int(&self, int: i64) -> StructValue { - self.new_value(ValueTag::Int, self.const_int(int).into()) - } - - pub fn const_float(&self, float: f64) -> FloatValue { - self.float_type.const_float(float) - } - - pub fn new_float(&self, float: f64) -> StructValue { - self.new_value(ValueTag::Float, self.const_float(float).into()) - } - - pub fn const_bool(&self, bool: bool) -> IntValue { - self.bool_type.const_int(bool as _, false) - } - - pub fn new_bool(&self, bool: bool) -> StructValue { - self.new_value(ValueTag::Bool, self.const_bool(bool).into()) - } - - pub fn new_null(&self) -> StructValue { - self.new_value(ValueTag::Null, self.int_type.const_zero().into()) - } -} - -extern "C" fn helper_call( - func: JITValue, - args: *mut JITValue, - len: usize, - mut engine: NonNull, - mut env: NonNull, -) -> JITValue { - let mut func = Value::from(func); +pub extern "C" fn helper_call( + func: &mut Value, + args_ptr: *mut Value, + args_len: usize, + engine: &mut Engine, + env: &mut Env, +) { // TODO: Error Handling - let args = core::ptr::slice_from_raw_parts_mut(args, len); + let args = core::ptr::slice_from_raw_parts_mut(args_ptr, args_len); let args = unsafe { Box::from_raw(args) }; func.call( args.into_iter().map(Value::from).collect(), - unsafe { engine.as_mut() }, - unsafe { env.as_mut() }, + engine, + env ) .unwrap(); - func.into() } -extern "C" fn helper_lookup_arg(env: NonNull, level: u64) -> JITValue { - let env_ref = unsafe { env.as_ref() }; - env_ref.lookup_arg(level as usize).into() +pub extern "C" fn helper_lookup_arg(env: &Env, level: usize, ret: &mut MaybeUninit) { + ret.write(env.lookup_arg(level as usize)); } -extern "C" fn helper_lookup(env: NonNull, ptr: *const u8, len: usize) -> JITValue { - let env = unsafe { env.as_ref() }; +pub extern "C" fn helper_lookup(env: &Env, sym_ptr: *const u8, sym_len: usize, ret: &mut MaybeUninit) { // TODO: Error Handling - let val: JITValue = env - .lookup_with(unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }) - .unwrap() - .clone() - .into(); - val -} - -extern "C" fn helper_force( - thunk: JITValue, - vm: NonNull, - jit: *const JITContext, -) -> JITValue { - if !matches!(thunk.tag, ValueTag::Thunk) { - return thunk; + unsafe { + ret.write(env + .lookup_with(str::from_utf8_unchecked(slice::from_raw_parts(sym_ptr, sym_len))) + .unwrap()); } - todo!() } -unsafe extern "C" fn helper_alloc_array(len: usize) -> *mut u8 { - unsafe { alloc(Layout::array::(len).unwrap()) } +pub extern "C" fn helper_select(val: &mut Value, path_ptr: *mut Value, path_len: usize, engine: &mut Engine, env: &mut Env) { + let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len); + let path = unsafe { Box::from_raw(path) }; + val.select(path.into_iter().map(|mut val| { + val.force(engine, env)?.coerce_to_string(); + Ok(val.unwrap_string()) + })).unwrap(); +} + +pub extern "C" fn helper_select_with_default(val: &mut Value, path_ptr: *mut Value, path_len: usize, default: NonNull, engine: &mut Engine, env: &mut Env) { + let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len); + let path = unsafe { Box::from_raw(path) }; + val.select_with_default(path.into_iter().map(|mut val| { + val.force(engine, env)?.coerce_to_string(); + Ok(val.unwrap_string()) + }), unsafe { default.read() }).unwrap(); +} + +pub extern "C" fn helper_force( + thunk: &mut Value, + engine: &mut Engine, + env: &mut Env, +) { + thunk.force(engine, env).unwrap(); +} + +pub extern "C" fn helper_eq(lhs: &mut Value, rhs: &Value) { + lhs.eq(rhs); +} + +pub unsafe extern "C" fn helper_create_string(ptr: *const u8, len: usize, ret: &mut MaybeUninit) { + unsafe { + ret.write(Value::String(str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)).to_string())); + } +} + +pub unsafe extern "C" fn helper_alloc_array(len: usize) -> *mut u8 { + unsafe { alloc(Layout::array::(len).unwrap()) } +} + +pub extern "C" fn helper_dbg(value: &Value) { + // dbg!(value); + println!("{value:?}") } diff --git a/src/eval/jit/mod.rs b/src/eval/jit/mod.rs index 5025e9d..18754ff 100644 --- a/src/eval/jit/mod.rs +++ b/src/eval/jit/mod.rs @@ -1,12 +1,8 @@ -use std::marker::PhantomData; -use std::ops::Deref; -use std::rc::Rc; - use cranelift::codegen::ir::Function; +use cranelift::codegen::ir::{self, ArgumentExtension, ArgumentPurpose, StackSlot}; use cranelift::prelude::*; -use cranelift::codegen::ir; -use cranelift_module::{DataDescription, Linkage, Module}; -use cranelift_jit::{JITModule, JITBuilder}; +use cranelift_jit::{JITBuilder, JITModule}; +use cranelift_module::{FuncId, Linkage, Module}; use crate::engine::Engine; use crate::env::Env; @@ -17,116 +13,229 @@ mod compile; mod helpers; pub use compile::JITCompile; -use helpers::Helpers; +use helpers::*; -#[repr(transparent)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ValueTag(u64); +pub type JITFunc = unsafe extern "C" fn(*const Engine, *const Env, *mut Value); -#[allow(non_upper_case_globals)] -#[allow(non_snake_case)] -impl ValueTag { - const Null: Self = Self(0); - const Int: Self = Self(1); - const Float: Self = Self(2); - const Path: Self = Self(3); - const Bool: Self = Self(4); - const AttrSet: Self = Self(5); - const List: Self = Self(6); - const Function: Self = Self(7); - const Thunk: Self = Self(8); +pub struct JITContext<'comp, 'ctx> { + pub compiler: &'comp mut JITCompiler, + pub builder: FunctionBuilder<'ctx>, +} - pub fn String(len: usize) -> Self { - Self(len as u64 ^ (1 << 31)) +impl<'comp, 'ctx> JITContext<'comp, 'ctx> { + fn new(compiler: &'comp mut JITCompiler, builder: FunctionBuilder<'ctx>) -> Self { + Self { compiler, builder } } - pub fn is_str(&self) -> bool { - self.0 >> 31 != 0 + fn alloc_array(&mut self, len: usize) -> ir::Value { + let len = self + .builder + .ins() + .iconst(self.compiler.ptr_type, len as i64); + let alloc_array = self + .compiler + .module + .declare_func_in_func(self.compiler.alloc_array, self.builder.func); + let inst = self.builder.ins().call(alloc_array, &[len]); + self.builder.inst_results(inst)[0] + } + + fn create_string(&mut self, string: &str) -> StackSlot { + let ptr = self + .builder + .ins() + .iconst(self.compiler.ptr_type, string.as_ptr() as i64); + let len = self + .builder + .ins() + .iconst(self.compiler.ptr_type, string.len() as i64); + let create_string = self + .compiler + .module + .declare_func_in_func(self.compiler.create_string, self.builder.func); + let slot = StackSlotData::new(StackSlotKind::ExplicitSlot, 32, 3); + let slot = self.builder.create_sized_stack_slot(slot); + let ret = self + .builder + .ins() + .stack_addr(self.compiler.ptr_type, slot, 0); + self.builder.ins().call(create_string, &[ptr, len, ret]); + slot + } + + fn dbg(&mut self, slot: StackSlot) { + let ptr = self + .builder + .ins() + .stack_addr(self.compiler.ptr_type, slot, 0); + let dbg = self + .compiler + .module + .declare_func_in_func(self.compiler.dbg, self.builder.func); + self.builder.ins().call(dbg, &[ptr]); + } + + fn call( + &mut self, + func: StackSlot, + args_ptr: ir::Value, + args_len: usize, + engine: ir::Value, + env: ir::Value, + ) { + let args_len = self + .builder + .ins() + .iconst(self.compiler.ptr_type, args_len as i64); + let call = self + .compiler + .module + .declare_func_in_func(self.compiler.call, self.builder.func); + let func = self + .builder + .ins() + .stack_addr(self.compiler.ptr_type, func, 0); + self.builder + .ins() + .call(call, &[func, args_ptr, args_len, engine, env]); + } + + fn lookup_arg(&mut self, env: ir::Value, idx: usize) -> StackSlot { + let slot = StackSlotData::new(StackSlotKind::ExplicitSlot, 32, 3); + let slot = self.builder.create_sized_stack_slot(slot); + let lookup_arg = self + .compiler + .module + .declare_func_in_func(self.compiler.lookup_arg, self.builder.func); + let idx = self + .builder + .ins() + .iconst(self.compiler.ptr_type, idx as i64); + let ptr = self + .builder + .ins() + .stack_addr(self.compiler.ptr_type, slot, 0); + self.builder.ins().call(lookup_arg, &[env, idx, ptr]); + slot + } + + fn select( + &mut self, + slot: StackSlot, + path_ptr: ir::Value, + path_len: usize, + engine: ir::Value, + env: ir::Value, + ) { + let select = self + .compiler + .module + .declare_func_in_func(self.compiler.select, self.builder.func); + let path_len = self + .builder + .ins() + .iconst(self.compiler.ptr_type, path_len as i64); + let ptr = self + .builder + .ins() + .stack_addr(self.compiler.ptr_type, slot, 0); + self.builder + .ins() + .call(select, &[ptr, path_ptr, path_len, engine, env]); + } + + fn select_with_default( + &mut self, + slot: StackSlot, + path_ptr: ir::Value, + path_len: usize, + default: StackSlot, + engine: ir::Value, + env: ir::Value, + ) { + let select_with_default = self + .compiler + .module + .declare_func_in_func(self.compiler.select_with_default, self.builder.func); + let path_len = self + .builder + .ins() + .iconst(self.compiler.ptr_type, path_len as i64); + let ptr = self + .builder + .ins() + .stack_addr(self.compiler.ptr_type, slot, 0); + let default_ptr = self + .builder + .ins() + .stack_addr(self.compiler.ptr_type, default, 0); + self.builder.ins().call( + select_with_default, + &[ptr, path_ptr, path_len, default_ptr, engine, env], + ); + } + + pub fn force(&mut self, slot: StackSlot, engine: ir::Value, env: ir::Value) { + let force = self + .compiler + .module + .declare_func_in_func(self.compiler.force, self.builder.func); + let ptr = self + .builder + .ins() + .stack_addr(self.compiler.ptr_type, slot, 0); + self.builder.ins().call(force, &[ptr, engine, env]); + } + + pub fn eq(&mut self, lhs: StackSlot, rhs: StackSlot) { + let lhs = self + .builder + .ins() + .stack_addr(self.compiler.ptr_type, lhs, 0); + let rhs = self + .builder + .ins() + .stack_addr(self.compiler.ptr_type, rhs, 0); + let eq = self.compiler.module.declare_func_in_func(self.compiler.eq, self.builder.func); + self.builder.ins().call(eq, &[lhs, rhs]); + } + + pub fn get_tag(&mut self, slot: StackSlot) -> ir::Value { + self.builder.ins().stack_load(types::I64, slot, 0) + } + + pub fn get_small_value(&mut self, ty: Type, slot: StackSlot) -> ir::Value { + self.builder.ins().stack_load(ty, slot, 8) } } -#[repr(C)] -#[derive(Clone, Copy)] -pub struct JITValue { - tag: ValueTag, - data: JITValueData, -} - -#[repr(C)] -#[derive(Clone, Copy)] -pub union JITValueData { - int: i64, - float: f64, - bool: bool, - ptr: *const (), -} - -impl From for Value { - fn from(value: JITValue) -> Self { - match value.tag { - ValueTag::Int => Value::Int(unsafe { value.data.int }), - ValueTag::Null => Value::Null, - ValueTag::Function => Value::Func(unsafe { value.data.int as usize }), - ValueTag::Thunk => Value::Thunk(unsafe { value.data.int as usize }), - _ => todo!("not implemented for {:?}", value.tag), - } - } -} - -impl From for JITValue { - fn from(value: Value) -> Self { - match value { - Value::Int(int) => JITValue { - tag: ValueTag::Int, - data: JITValueData { int }, - }, - Value::List(list) => JITValue { - tag: ValueTag::List, - data: JITValueData { - ptr: list.as_ptr() as *const (), - }, - }, - Value::Func(idx) => JITValue { - tag: ValueTag::Function, - data: JITValueData { int: idx as i64 }, - }, - Value::Thunk(idx) => JITValue { - tag: ValueTag::Thunk, - data: JITValueData { int: idx as i64 }, - }, - _ => todo!(), - } - } -} - -pub struct JITFunc<'exec>(F<'exec>, PhantomData<&'exec mut ()>); -type F<'exec> = unsafe extern "C" fn(*const Engine<'exec>, *const Env, *mut JITValue); - -impl<'exec> From> for JITFunc<'exec> { - fn from(value: F<'exec>) -> Self { - Self(value, PhantomData) - } -} - -impl<'exec> Deref for JITFunc<'exec> { - type Target = F<'exec>; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -pub struct JITContext { - func_builder: FunctionBuilderContext, - +pub struct JITCompiler { ctx: codegen::Context, - data_description: DataDescription, module: JITModule, - func: Function, + builder_ctx: Option, - helpers: Helpers, + int_type: Type, + float_type: Type, + bool_type: Type, + ptr_type: Type, + value_type: Type, + func_sig: Signature, + + call: FuncId, + lookup_arg: FuncId, + lookup: FuncId, + select: FuncId, + select_with_default: FuncId, + force: FuncId, + + eq: FuncId, + + alloc_array: FuncId, + create_string: FuncId, + dbg: FuncId, } -impl JITContext { +impl JITCompiler { pub fn new() -> Self { let mut flag_builder = settings::builder(); flag_builder.set("use_colocated_libcalls", "false").unwrap(); @@ -137,69 +246,247 @@ impl JITContext { let isa = isa_builder .finish(settings::Flags::new(flag_builder)) .unwrap(); - let builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); + let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); + + builder.symbol("helper_call", helper_call as _); + builder.symbol("helper_lookup_arg", helper_lookup_arg as _); + builder.symbol("helper_lookup", helper_lookup as _); + builder.symbol("helper_select", helper_select as _); + builder.symbol( + "helper_select_with_default", + helper_select_with_default as _, + ); + builder.symbol("helper_force", helper_force as _); + builder.symbol("helper_eq", helper_eq as _); + + builder.symbol("helper_alloc_array", helper_alloc_array as _); + builder.symbol("helper_create_string", helper_create_string as _); + builder.symbol("helper_dbg", helper_dbg as _); let mut module = JITModule::new(builder); let ctx = module.make_context(); + let int_type = types::I64; + let float_type = types::F64; + let bool_type = types::I8; + let ptr_type = module.target_config().pointer_type(); + let value_type = types::I128; + + // fn(*const Engine, *const Env, *mut Value) + let mut func_sig = module.make_signature(); + func_sig.params.extend( + [AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }; 3], + ); + + // fn(func: &mut Value, args_ptr: *mut Value, args_len: usize, engine: &mut Engine, env: + // &mut Env) + let mut call_sig = module.make_signature(); + call_sig.params.extend( + [AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }; 5], + ); + let call = module + .declare_function("helper_call", Linkage::Import, &call_sig) + .unwrap(); + + // fn(env: &Env, level: usize, ret: &mut MaybeUninit) + let mut lookup_arg_sig = module.make_signature(); + lookup_arg_sig.params.extend( + [AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }; 3], + ); + let lookup_arg = module + .declare_function("helper_lookup_arg", Linkage::Import, &lookup_arg_sig) + .unwrap(); + + // fn(env: &Env, sym_ptr: *const u8, sym_len: usize, ret: &mut MaybeUninit) + let mut lookup_sig = module.make_signature(); + lookup_sig.params.extend( + [AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }; 4], + ); + let lookup = module + .declare_function("helper_lookup", Linkage::Import, &lookup_sig) + .unwrap(); + + // fn(val: &mut Value, path_ptr: *mut Value, path_len: usize, engine: &mut Engine, env: &mut Env) + let mut select_sig = module.make_signature(); + select_sig.params.extend( + [AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }; 5], + ); + let select = module + .declare_function("helper_select", Linkage::Import, &select_sig) + .unwrap(); + + // fn(val: &mut Value, path_ptr: *mut Value, path_len: usize, default: NonNull, engine: &mut Engine, env: &mut Env) + let mut select_with_default_sig = module.make_signature(); + select_with_default_sig.params.extend( + [AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }; 6], + ); + let select_with_default = module + .declare_function( + "helper_select_with_default", + Linkage::Import, + &select_with_default_sig, + ) + .unwrap(); + + // fn(thunk: &mut Value, engine: &mut Engine, env: &mut Env) + let mut force_sig = module.make_signature(); + force_sig.params.extend( + [AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }; 3], + ); + let force = module + .declare_function("helper_force", Linkage::Import, &force_sig) + .unwrap(); + + let mut eq_sig = module.make_signature(); + eq_sig.params.extend( + [AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }; 2], + ); + let eq = module + .declare_function("helper_eq", Linkage::Import, &eq_sig) + .unwrap(); + + let mut alloc_array_sig = module.make_signature(); + alloc_array_sig.params.push(AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }); + alloc_array_sig.returns.push(AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }); + let alloc_array = module + .declare_function("helper_alloc_array", Linkage::Import, &alloc_array_sig) + .unwrap(); + + let mut create_string_sig = module.make_signature(); + create_string_sig.params.extend( + [AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }; 3], + ); + let create_string = module + .declare_function("helper_create_string", Linkage::Import, &create_string_sig) + .unwrap(); + + let mut dbg_sig = module.make_signature(); + dbg_sig.params.push(AbiParam { + value_type: ptr_type, + purpose: ArgumentPurpose::Normal, + extension: ArgumentExtension::None, + }); + let dbg = module + .declare_function("helper_dbg", Linkage::Import, &dbg_sig) + .unwrap(); + Self { - func_builder: FunctionBuilderContext::new(), - helpers: Helpers::new(&ctx, &mut module), - data_description: DataDescription::new(), - func: Function::new(), + builder_ctx: None, ctx, module, + + int_type, + float_type, + bool_type, + ptr_type, + value_type, + func_sig, + + call, + lookup_arg, + lookup, + select, + select_with_default, + force, + + eq, + + alloc_array, + create_string, + dbg, } } - pub fn compile<'exec>(&'exec mut self, ir: &Ir, id: usize) -> JITFunc<'exec> { - let func_id = self.module.declare_function(format!("nixjit_thunk{id}").as_str(), Linkage::Local, &self.helpers.func_sig).unwrap(); - let mut func = Function::new(); - let builder = FunctionBuilder::new(&mut func, &mut self.func_builder); - let entry = builder.create_block(); - builder.switch_to_block(entry); - // TODO: - let ret = ir.compile(self, func_id, &mut Vec::new()); - self.func_builder - .build_store(func_id.get_nth_param(2).unwrap().into_pointer_value(), ret) - .unwrap(); - self.func_builder.build_return(None).unwrap(); - self.module.print_to_stderr(); - let _ = self.execution_engine.remove_module(&self.module); - let _ = self.execution_engine.add_module(&self.module); - if func_id.verify(true) { - unsafe { - JITFunc( - std::mem::transmute( - self.execution_engine - .get_function_address(func_id.get_name().to_str().unwrap()) - .unwrap(), - ), - PhantomData, - ) - } - } else { - todo!() - } - } - - pub fn get_value(&self, builder: &mut FunctionBuilder, val: ir::Value) -> ir::Value { - let offset = builder.ins().iconst(types::I8, 64); - builder.ins().rotl(val, offset) - } - - pub fn get_tag(&self, builder: &mut FunctionBuilder, val: ir::Value) -> ir::Value { - let offset = builder.ins().iconst(types::I8, 64); - builder.ins().rotl(val, offset) - } - - pub fn const_ptr(&self, ptr: *const ()) -> PointerValue { - self.func_builder - .build_int_to_ptr( - self.helpers.int_type.const_int(ptr as _, false), - self.helpers.ptr_type, - "ptrconv", + pub fn compile(&mut self, ir: &Ir, id: usize) -> JITFunc { + let func_id = self + .module + .declare_function( + format!("nixjit_thunk{id}").as_str(), + Linkage::Local, + &self.func_sig, ) - .unwrap() + .unwrap(); + let mut func = Function::new(); + func.signature = self.func_sig.clone(); + let mut builder_ctx = self.builder_ctx.take().unwrap_or_default(); + let mut ctx = JITContext::new(self, FunctionBuilder::new(&mut func, &mut builder_ctx)); + + let entry = ctx.builder.create_block(); + ctx.builder.append_block_params_for_function_params(entry); + ctx.builder.switch_to_block(entry); + + let params = ctx.builder.block_params(entry); + let engine = params[0]; + let env = params[1]; + let ret = params[2]; + let res = ir.compile(&mut ctx, engine, env); + + let tag = ctx.builder.ins().stack_load(types::I64, res, 0); + let val0 = ctx.builder.ins().stack_load(types::I64, res, 8); + let val1 = ctx.builder.ins().stack_load(types::I64, res, 16); + let val2 = ctx.builder.ins().stack_load(types::I64, res, 24); + ctx.builder.ins().store(MemFlags::new(), tag, ret, 0); + ctx.builder.ins().store(MemFlags::new(), val0, ret, 8); + ctx.builder.ins().store(MemFlags::new(), val1, ret, 16); + ctx.builder.ins().store(MemFlags::new(), val2, ret, 24); + ctx.builder.ins().return_(&[]); + ctx.builder.seal_all_blocks(); + ctx.builder.finalize(); + + println!("{:?}", ir); + println!("{}", func.display()); + self.ctx.func = func; + self.module.define_function(func_id, &mut self.ctx).unwrap(); + self.module.finalize_definitions().unwrap(); + self.ctx.clear(); + + let _ = self.builder_ctx.insert(builder_ctx); + unsafe { + std::mem::transmute::<*const u8, JITFunc>(self.module.get_finalized_function(func_id)) + } } } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 662e2de..d80b30a 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -87,9 +87,9 @@ impl Evaluate for ir::BinOp { } Mul => lhs.mul(rhs), Div => lhs.div(rhs)?, - Eq => Value::eq(&mut lhs, rhs), + Eq => Value::eq(&mut lhs, &rhs), Neq => { - Value::eq(&mut lhs, rhs); + Value::eq(&mut lhs, &rhs); lhs.not(); } Lt => lhs.lt(rhs), @@ -197,9 +197,7 @@ impl Evaluate for ir::If { impl Evaluate for ir::LoadFunc { fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { - let idx = engine.func_offset + self.idx; - let result = Value::Func(idx).ok(); - Ok(result.unwrap()) + Value::Func(self.idx).ok() } } @@ -207,14 +205,14 @@ impl Evaluate for ir::Call { fn eval(&self, engine: &mut Engine, env: &mut Env) -> Result { let mut func = self.func.eval(engine, env)?; func.force(engine, env)?; - let result = func.call( + func.call( self.args .iter() .map(|arg| arg.eval(engine, env)) .collect::>()?, engine, env, - ); + )?; Ok(func.ok().unwrap()) } } @@ -264,7 +262,7 @@ impl Evaluate for ir::ConcatStrings { } } -impl Evaluate for ir::String { +impl Evaluate for ir::Str { fn eval(&self, _: &mut Engine, _: &mut Env) -> Result { let result = Value::String(self.val.clone()).ok(); Ok(result.unwrap()) diff --git a/src/ir/ctx.rs b/src/ir/ctx.rs index 750dff3..070cc4a 100644 --- a/src/ir/ctx.rs +++ b/src/ir/ctx.rs @@ -1,6 +1,5 @@ use derive_more::Unwrap; -use ecow::EcoString; -use hashbrown::{HashMap, HashSet}; +use hashbrown::HashMap; use crate::error::Result; use crate::ty::common::Const; @@ -15,8 +14,8 @@ pub enum Index { pub struct DowngradeContext { pub thunks: Vec<(Ir, bool)>, - pub thunk_deps: Vec>, - pub func_deps: Vec>, + pub thunk_deps: Vec>, + pub func_deps: Vec>, pub func_arg_dep: Vec, pub funcs: Vec, } @@ -27,10 +26,10 @@ pub struct Env<'a, 'env> { } enum EnvNode<'a> { - Builtins(&'a HashMap), - Let(&'a Vec<(EcoString, MaybeThunk)>), - SingleArg(EcoString), - MultiArg(HashMap>, Option), + Builtins(&'a HashMap), + Let(&'a Vec<(String, MaybeThunk)>), + SingleArg(String), + MultiArg(HashMap>, Option), With, } @@ -44,21 +43,21 @@ pub enum LookupResult { } impl<'a, 'env> Env<'a, 'env> { - pub fn new(base: &'a HashMap) -> Self { + pub fn new(builtins: &'a HashMap) -> Self { Self { - env: EnvNode::Builtins(base), + env: EnvNode::Builtins(builtins), prev: None, } } - pub fn enter_let(&'env self, map: &'a Vec<(EcoString, MaybeThunk)>) -> Self { + pub fn enter_let(&'env self, map: &'a Vec<(String, MaybeThunk)>) -> Self { Self { env: EnvNode::Let(map), prev: Some(self), } } - pub fn enter_single_arg(&'env self, ident: EcoString) -> Self { + pub fn enter_single_arg(&'env self, ident: String) -> Self { Self { env: EnvNode::SingleArg(ident), prev: Some(self), @@ -67,8 +66,8 @@ impl<'a, 'env> Env<'a, 'env> { pub fn enter_multi_arg( &'env self, - map: HashMap>, - alias: Option, + map: HashMap>, + alias: Option, ) -> Self { Self { env: EnvNode::MultiArg(map, alias), @@ -85,7 +84,7 @@ impl<'a, 'env> Env<'a, 'env> { fn _lookup( &self, - ident: &EcoString, + ident: &String, mut arg_idx: usize, has_with: bool, ) -> core::result::Result { @@ -132,7 +131,7 @@ impl<'a, 'env> Env<'a, 'env> { .map_or_else(|| unreachable!(), |x| x) } - pub fn lookup(&self, ident: &EcoString) -> core::result::Result { + pub fn lookup(&self, ident: &String) -> core::result::Result { self._lookup(ident, 0, false) } } @@ -153,38 +152,38 @@ impl DowngradeContext { pub fn new_thunk(&mut self, thunk: Ir) -> Thunk { let idx = self.thunks.len(); self.thunks.push((thunk, false)); - self.thunk_deps.push(HashSet::new()); + self.thunk_deps.push(HashMap::new()); Thunk { idx } } pub fn maybe_thunk(&mut self, ir: Ir) -> MaybeThunk { match ir { Ir::Const(cnst) => MaybeThunk::Const(cnst), - Ir::String(string) => MaybeThunk::String(string), + Ir::Str(string) => MaybeThunk::String(string), Ir::Thunk(thunk) => MaybeThunk::Thunk(thunk), ir => MaybeThunk::Thunk(self.new_thunk(ir)), } } - pub fn new_dep(&mut self, this: Index, dep: Dep) -> Result<()> { + pub fn new_dep(&mut self, this: Index, dep: Dep) -> usize { match this { Index::Thunk(idx) => { - /* if dep == Dep::Thunk(idx) && !matches!(self.thunks[idx].0, Ir::List(_)) { - return Err(Error::DowngradeError( - "infinite recursion encountered".into(), - )); - } */ - self.thunk_deps[idx].insert(dep.unwrap_thunk()) + let len = self.thunk_deps.len(); + self.thunk_deps[idx].entry(dep.unwrap_thunk()).or_insert(len); + len } - Index::Func(idx) => self.func_deps[idx].insert(dep), - }; - Ok(()) + Index::Func(idx) => { + let len = self.thunk_deps.len(); + self.func_deps[idx].entry(dep).or_insert(len); + len + }, + } } pub fn new_func(&mut self, func: Func) -> LoadFunc { let idx = self.funcs.len(); self.funcs.push(func); - self.func_deps.push(HashSet::new()); + self.func_deps.push(HashMap::new()); self.func_arg_dep.push(false); LoadFunc { idx } } @@ -202,7 +201,7 @@ impl DowngradeContext { core::ptr::write( func, Func { - param: crate::ir::Param::Ident(EcoString::new()), + param: crate::ir::Param::Ident(String::new()), body: super::Const { val: Const::Null }.ir().boxed(), }, ); @@ -246,8 +245,8 @@ impl DowngradeContext { #[derive(Debug)] pub struct Downgraded { pub thunks: Box<[Ir]>, - pub func_offset: usize, - pub func_deps: Vec>, + pub funcs: Box<[Ir]>, + pub func_deps: Vec>, pub graph: Vec, } @@ -255,14 +254,13 @@ impl Downgraded { pub fn new(ctx: DowngradeContext) -> Self { Self { graph: SccAnalyzer::new(&ctx).analyze(), - func_offset: ctx.thunks.len(), func_deps: ctx.func_deps, thunks: ctx .thunks .into_iter() .map(|(ir, _)| ir) - .chain(ctx.funcs.into_iter().map(|Func { body, .. }| *body)) .collect(), + funcs: ctx.funcs.into_iter().map(|Func { body, .. }| *body).collect() } } } diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 60df80d..01c1836 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -1,7 +1,5 @@ use derive_more::{IsVariant, TryUnwrap, Unwrap}; -use ecow::EcoString; use hashbrown::HashMap; -use cranelift::codegen; use itertools::Itertools; use rnix::ast::HasEntry; use rnix::ast::{self, Expr}; @@ -68,14 +66,14 @@ macro_rules! ir { impl Ir { #[inline] - fn as_ref(&self) -> IrRef { + fn as_ref(&self) -> IrRef<'_> { match self { $(Ir::$ty(ir) => IrRef::$ty(ir),)* } } #[inline] - fn as_mut(&mut self) -> IrMut { + fn as_mut(&mut self) -> IrMut<'_> { match self { $(Ir::$ty(ir) => IrMut::$ty(ir),)* } @@ -90,9 +88,9 @@ macro_rules! ir { } impl JITCompile for Ir { - fn compile(&self, ctx: &mut JITContext, builder: &mut FunctionBuilder) -> StructValue{ + fn compile(&self, ctx: &mut JITContext, engine: cranelift::prelude::Value, env: cranelift::prelude::Value) -> cranelift::codegen::ir::StackSlot { match self { - $(Ir::$ty(ir) => ir.compile(ctx, builder),)* + $(Ir::$ty(ir) => ir.compile(ctx, engine, env),)* } } } @@ -126,7 +124,7 @@ macro_rules! ir { } ir! { - Attrs => { stcs: HashMap, dyns: Vec }, + Attrs => { stcs: HashMap, dyns: Vec }, List => { items: Vec }, HasAttr => { lhs: Box, rhs: Vec }, BinOp => { lhs: Box, rhs: Box, kind: BinOpKind }, @@ -136,14 +134,14 @@ ir! { LoadFunc => { idx: usize }, Call => { func: Box, args: Vec }, - Let => { bindings: Vec<(EcoString, MaybeThunk)>, expr: Box }, + Let => { bindings: Vec<(String, MaybeThunk)>, expr: Box }, With => { namespace: Box, expr: Box }, Assert => { assertion: Box, expr: Box }, ConcatStrings => { parts: Vec }, #[derive(Copy)] Const => { val: c::Const }, - String => { val: EcoString }, - Var => { sym: EcoString }, + Str => { val: String }, + Var => { sym: String }, #[derive(Copy)] Arg => { level: usize }, #[derive(Copy)] @@ -168,7 +166,7 @@ impl Ir { #[derive(Debug, Clone)] pub enum MaybeThunk { Const(Const), - String(String), + String(Str), Thunk(Thunk), } @@ -340,7 +338,7 @@ impl Attrs { pub enum Attr { Dynamic(Ir), Strs(ConcatStrings), - Str(EcoString), + Str(String), } impl Attr { @@ -365,7 +363,7 @@ impl Thunk { ctx: &mut DowngradeContext, env: &Env<'a, 'env>, ) -> Result { - ctx.new_dep(self_idx, Dep::Thunk(self.idx))?; + ctx.new_dep(self_idx, Dep::Thunk(self.idx)); ctx.resolve_thunk(self.idx, env)?; self.ir().ok() } @@ -442,11 +440,11 @@ pub struct Func { #[derive(Clone, Debug)] pub enum Param { - Ident(EcoString), + Ident(String), Formals { - formals: Vec<(EcoString, Option)>, + formals: Vec<(String, Option)>, ellipsis: bool, - alias: Option, + alias: Option, }, } @@ -557,7 +555,7 @@ impl Downgrade for ast::Path { let parts = self .parts() .map(|part| match part { - ast::InterpolPart::Literal(lit) => String { + ast::InterpolPart::Literal(lit) => Str { val: lit.to_string().into(), } .ir() @@ -602,7 +600,7 @@ impl Downgrade for ast::Str { .normalized_parts() .into_iter() .map(|part| match part { - ast::InterpolPart::Literal(lit) => String { val: lit.into() }.ir().ok(), + ast::InterpolPart::Literal(lit) => Str { val: lit.into() }.ir().ok(), ast::InterpolPart::Interpolation(interpol) => { interpol.expr().unwrap().downgrade(ctx) } @@ -640,7 +638,7 @@ impl Downgrade for ast::Literal { match self.kind() { ast::LiteralKind::Integer(int) => Const::from(int.value().unwrap()).ir(), ast::LiteralKind::Float(float) => Const::from(float.value().unwrap()).ir(), - ast::LiteralKind::Uri(uri) => String { + ast::LiteralKind::Uri(uri) => Str { val: uri.to_string().into(), } .ir(), @@ -660,7 +658,7 @@ impl Const { } } -impl String { +impl Str { fn resolve<'a, 'env>( self, _: Index, diff --git a/src/ir/scc.rs b/src/ir/scc.rs index c061300..d0eb72b 100644 --- a/src/ir/scc.rs +++ b/src/ir/scc.rs @@ -9,7 +9,7 @@ pub struct SccNode { pub deps: HashSet, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Unwrap)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Unwrap)] pub enum Dep { Thunk(usize), Arg(usize), @@ -43,7 +43,7 @@ impl SccGraph { for (from_node_id, from_deps) in ctx.thunk_deps.iter().enumerate() { let from_scc_id = thunk_to_scc[&from_node_id]; - for &to_node_id in from_deps { + for (&to_node_id, _) in from_deps { let to_scc_id = thunk_to_scc[&to_node_id]; if from_scc_id != to_scc_id { graph @@ -166,7 +166,7 @@ impl<'ctx> SccAnalyzer<'ctx> { self.on_stack.insert(v_id); if let Some(deps) = self.ctx.thunk_deps.get(v_id) { - for &w_id in deps { + for (&w_id, _) in deps { if !self.indices.contains_key(&w_id) { self.strong_connect(w_id); let v_low_link = *self.low_links.get(&v_id).unwrap(); diff --git a/src/ir/utils.rs b/src/ir/utils.rs index 03bed34..742ed8a 100644 --- a/src/ir/utils.rs +++ b/src/ir/utils.rs @@ -55,7 +55,7 @@ pub fn downgrade_attrs(has_entry: impl ast::HasEntry, ctx: &mut DowngradeContext pub fn downgrade_inherit( inherit: ast::Inherit, - stcs: &mut HashMap, + stcs: &mut HashMap, ctx: &mut DowngradeContext, ) -> Result<()> { let from = if let Some(from) = inherit.from() { @@ -110,7 +110,7 @@ pub fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result String { val: lit.into() }.ir().ok(), + Literal(lit) => self::Str { val: lit.into() }.ir().ok(), Interpolation(interpol) => interpol.expr().unwrap().downgrade(ctx), }) .collect::>>()?; diff --git a/src/ty/common.rs b/src/ty/common.rs index 05bbf95..9ffad6c 100644 --- a/src/ty/common.rs +++ b/src/ty/common.rs @@ -2,14 +2,13 @@ use core::fmt::{Display, Formatter, Result as FmtResult}; use core::hash::Hash; use derive_more::{Constructor, IsVariant, Unwrap}; -use ecow::EcoString; #[derive(Clone, Debug, PartialEq, Constructor, Hash)] pub struct Catchable { - msg: EcoString, + msg: String, } -impl> From for Catchable { +impl> From for Catchable { fn from(value: T) -> Self { Catchable { msg: value.into() } } diff --git a/src/ty/internal/attrset.rs b/src/ty/internal/attrset.rs index b4c7506..51abad0 100644 --- a/src/ty/internal/attrset.rs +++ b/src/ty/internal/attrset.rs @@ -2,7 +2,6 @@ use core::ops::Deref; use std::rc::Rc; use derive_more::Constructor; -use ecow::EcoString; use hashbrown::{HashMap, HashSet}; use itertools::Itertools; @@ -16,17 +15,17 @@ use super::Value; #[repr(transparent)] #[derive(Constructor, Clone, PartialEq, Debug)] pub struct AttrSet { - data: HashMap, + data: HashMap, } -impl From> for AttrSet { - fn from(data: HashMap) -> Self { +impl From> for AttrSet { + fn from(data: HashMap) -> Self { Self { data } } } impl Deref for AttrSet { - type Target = HashMap; + type Target = HashMap; fn deref(&self) -> &Self::Target { &self.data } @@ -39,11 +38,11 @@ impl AttrSet { } } - pub fn push_attr_force(&mut self, sym: EcoString, val: Value) { + pub fn push_attr_force(&mut self, sym: String, val: Value) { self.data.insert(sym, val); } - pub fn push_attr(&mut self, sym: EcoString, val: Value) { + pub fn push_attr(&mut self, sym: String, val: Value) { if self.data.get(&sym).is_some() { todo!() } @@ -52,7 +51,7 @@ impl AttrSet { pub fn select( &self, - mut path: impl DoubleEndedIterator>, + mut path: impl DoubleEndedIterator>, ) -> Result { // .ok_or_else(|| Error::EvalError())), let mut data = &self.data; @@ -75,7 +74,7 @@ impl AttrSet { pub fn has_attr( &self, - mut path: impl DoubleEndedIterator>, + mut path: impl DoubleEndedIterator>, ) -> Result { let mut data = &self.data; let last = path.nth_back(0).unwrap(); @@ -94,15 +93,15 @@ impl AttrSet { } } - pub fn as_inner(&self) -> &HashMap { + pub fn as_inner(&self) -> &HashMap { &self.data } - pub fn into_inner(self: Rc) -> Rc> { + pub fn into_inner(self: Rc) -> Rc> { unsafe { core::mem::transmute(self) } } - pub fn from_inner(data: HashMap) -> Self { + pub fn from_inner(data: HashMap) -> Self { Self { data } } diff --git a/src/ty/internal/func.rs b/src/ty/internal/func.rs index 5106483..dd575ba 100644 --- a/src/ty/internal/func.rs +++ b/src/ty/internal/func.rs @@ -1,11 +1,6 @@ -use std::rc::Rc; - use derive_more::Constructor; use hashbrown::HashMap; -use crate::env::Env; -use crate::ir; - use super::Value; #[derive(Debug, Clone, Constructor)] diff --git a/src/ty/internal/list.rs b/src/ty/internal/list.rs index da02ea7..6e986b7 100644 --- a/src/ty/internal/list.rs +++ b/src/ty/internal/list.rs @@ -1,11 +1,9 @@ use std::ops::Deref; -use std::rc::Weak; use ecow::EcoVec; use hashbrown::HashSet; use crate::engine::Engine; -use crate::env::Env; use crate::ty::public as p; use super::Value; diff --git a/src/ty/internal/mod.rs b/src/ty/internal/mod.rs index 801b89c..6e79c47 100644 --- a/src/ty/internal/mod.rs +++ b/src/ty/internal/mod.rs @@ -2,7 +2,6 @@ use std::hash::Hash; use std::rc::Rc; use derive_more::{IsVariant, Unwrap}; -use ecow::EcoString; use func::PartialFunc; use hashbrown::HashSet; use replace_with::replace_with_or_abort; @@ -24,21 +23,22 @@ pub use attrset::*; pub use list::List; pub use primop::*; +#[repr(C, u64)] #[derive(IsVariant, Unwrap, Clone, Debug)] pub enum Value { - Int(i64), - Float(f64), - Bool(bool), - String(EcoString), - Null, - Thunk(usize), - AttrSet(Rc), - List(List), - Catchable(EcoString), - PrimOp(Rc), - PartialPrimOp(Rc), - Func(usize), - PartialFunc(Rc), + Int(i64) = Self::INT, + Float(f64) = Self::FLOAT, + Bool(bool) = Self::BOOL, + String(String) = Self::STRING, + Null = Self::NULL, + Thunk(usize) = Self::THUNK, + AttrSet(Rc) = Self::ATTRSET, + List(List) = Self::LIST, + Catchable(String) = Self::CATCHABLE, + PrimOp(Rc) = Self::PRIMOP, + PartialPrimOp(Rc) = Self::PARTIAL_PRIMOP, + Func(usize) = Self::FUNC, + PartialFunc(Rc) = Self::PARTIAL_FUNC, } impl Hash for Value { @@ -54,6 +54,20 @@ impl Hash for Value { } impl Value { + pub const INT: u64 = 0; + pub const FLOAT: u64 = 1; + pub const BOOL: u64 = 2; + pub const STRING: u64 = 3; + pub const NULL: u64 = 4; + pub const THUNK: u64 = 5; + pub const ATTRSET: u64 = 6; + pub const LIST: u64 = 7; + pub const CATCHABLE: u64 = 8; + pub const PRIMOP: u64 = 9; + pub const PARTIAL_PRIMOP: u64 = 10; + pub const FUNC: u64 = 11; + pub const PARTIAL_FUNC: u64 = 12; + fn eq_impl(&self, other: &Self) -> bool { use Value::*; match (self, other) { @@ -89,7 +103,7 @@ pub enum ValueAsRef<'v> { Int(i64), Float(f64), Bool(bool), - String(&'v EcoString), + String(&'v String), Null, Thunk(usize), AttrSet(&'v AttrSet), @@ -102,7 +116,7 @@ pub enum ValueAsRef<'v> { } impl Value { - pub fn as_ref(&self) -> ValueAsRef { + pub fn as_ref(&self) -> ValueAsRef<'_> { use Value::*; use ValueAsRef as R; match self { @@ -179,13 +193,13 @@ impl Value { env.enter_arg(args.next().unwrap()); let (ret, cache) = env.with_cache(std::mem::take(cache), |env| { engine.eval_func_deps(idx, env)?; - let mut ret = engine.eval_thunk(idx, env)?; + let mut ret = engine.call_func(idx, env)?; while args.peek().is_some() { match ret { Value::Func(func) => { env.enter_arg(args.next().unwrap()); engine.eval_func_deps(idx, env)?; - ret = engine.eval_thunk(func, env)?; + ret = engine.call_func(func, env)?; } Value::PartialFunc(_) => { todo!() @@ -219,16 +233,16 @@ impl Value { env.enter_arg(args.next().unwrap()); let (ret, cache) = env.with_new_cache(|env| { engine.eval_func_deps(idx, env)?; - let mut ret = engine.eval_thunk(idx, env)?; + let mut ret = engine.call_func(idx, env)?; ret.force(engine, env)?; while args.peek().is_some() { match ret { - Value::Func(func) => { + Value::Func(idx) => { env.enter_arg(args.next().unwrap()); engine.eval_func_deps(idx, env)?; - ret = engine.eval_thunk(func, env)?; + ret = engine.call_func(idx, env)?; } - Value::PartialFunc(mut func) => { + Value::PartialFunc(_) => { todo!() } Value::PrimOp(primop) => { @@ -288,12 +302,12 @@ impl Value { } } - pub fn eq(&mut self, other: Self) { + pub fn eq(&mut self, other: &Self) { use Value::Bool; *self = match (&*self, other) { (Value::Catchable(_), _) => return, - (_, x @ Value::Catchable(_)) => x, - (s, other) => Bool(s.eq_impl(&other)), + (_, x @ Value::Catchable(_)) => x.clone(), + (s, other) => Bool(s.eq_impl(other)), }; } @@ -365,30 +379,32 @@ impl Value { (Int(a), Float(b)) => Float(*a as f64 / b), (Float(a), Int(b)) => Float(a / b as f64), (Float(a), Float(b)) => Float(a / b), - (Value::Catchable(_), _) => return Ok(()), - (_, x @ Value::Catchable(_)) => x, + (Catchable(_), _) => return Ok(()), + (_, x @ Catchable(_)) => x, _ => todo!(), }; Ok(()) } pub fn concat_string(&mut self, mut other: Self) -> &mut Self { + use Value::*; match (self.coerce_to_string(), other.coerce_to_string()) { - (Value::String(a), Value::String(b)) => { + (String(a), String(b)) => { a.push_str(b.as_str()); } - (_, Value::Catchable(_)) => *self = other, - (Value::Catchable(_), _) => (), + (_, Catchable(_)) => *self = other, + (Catchable(_), _) => (), _ => todo!(), } self } pub fn push(&mut self, elem: Self) -> &mut Self { - if let Value::List(list) = self { + use Value::*; + if let List(list) = self { list.push(elem); - } else if let Value::Catchable(_) = self { - } else if let Value::Catchable(_) = elem { + } else if let Catchable(_) = self { + } else if let Catchable(_) = elem { *self = elem; } else { todo!() @@ -397,24 +413,26 @@ impl Value { } pub fn concat(&mut self, other: Self) { - if let x @ Value::Catchable(_) = other { + use Value::*; + if let x @ Catchable(_) = other { *self = x; return; } match (self, other) { - (Value::List(a), Value::List(b)) => { + (List(a), List(b)) => { a.concat(&b); } - (Value::Catchable(_), _) => (), + (Catchable(_), _) => (), _ => todo!(), } } - pub fn push_attr(&mut self, sym: EcoString, val: Self) -> &mut Self { - if let Value::AttrSet(attrs) = self { + pub fn push_attr(&mut self, sym: String, val: Self) -> &mut Self { + use Value::*; + if let AttrSet(attrs) = self { Rc::make_mut(attrs).push_attr(sym, val); - } else if let Value::Catchable(_) = self { - } else if let Value::Catchable(_) = val { + } else if let Catchable(_) = self { + } else if let Catchable(_) = val { *self = val } else { todo!() @@ -423,26 +441,28 @@ impl Value { } pub fn update(&mut self, other: Self) { - if let x @ Value::Catchable(_) = other { + use Value::*; + if let x @ Catchable(_) = other { *self = x; return; } match (self, other) { - (Value::AttrSet(a), Value::AttrSet(b)) => { + (AttrSet(a), AttrSet(b)) => { Rc::make_mut(a).update(&b); } - (Value::Catchable(_), _) => (), + (Catchable(_), _) => (), _ => todo!(), } } pub fn select( &mut self, - path: impl DoubleEndedIterator>, + path: impl DoubleEndedIterator>, ) -> Result<&mut Self> { + use Value::*; let val = match self { - Value::AttrSet(attrs) => attrs.select(path), - Value::Catchable(_) => return Ok(self), + AttrSet(attrs) => attrs.select(path), + Catchable(_) => return Ok(self), _ => Err(Error::EvalError(format!( "cannot select from {:?}", self.typename() @@ -454,12 +474,13 @@ impl Value { pub fn select_with_default( &mut self, - path: impl DoubleEndedIterator>, + path: impl DoubleEndedIterator>, default: Self, ) -> Result<&mut Self> { + use Value::*; let val = match self { - Value::AttrSet(attrs) => attrs.select(path).unwrap_or(default), - Value::Catchable(_) => return Ok(self), + AttrSet(attrs) => attrs.select(path).unwrap_or(default), + Catchable(_) => return Ok(self), _ => { return Err(Error::EvalError(format!( "cannot select from {:?}", @@ -473,21 +494,23 @@ impl Value { pub fn has_attr( &mut self, - path: impl DoubleEndedIterator>, + path: impl DoubleEndedIterator>, ) -> Result<()> { - if let Value::AttrSet(attrs) = self { - let val = Value::Bool(attrs.has_attr(path)?); + use Value::*; + if let AttrSet(attrs) = self { + let val = Bool(attrs.has_attr(path)?); *self = val; - } else if let Value::Catchable(_) = self { + } else if let Catchable(_) = self { } else { - *self = Value::Bool(false); + *self = Bool(false); } Ok(()) } pub fn coerce_to_string(&mut self) -> &mut Self { - if let Value::String(_) = self { - } else if let Value::Catchable(_) = self { + use Value::*; + if let String(_) = self { + } else if let Catchable(_) = self { } else { todo!() } @@ -496,7 +519,6 @@ impl Value { pub fn force(&mut self, engine: &mut Engine, env: &mut Env) -> Result<&mut Self> { if let &mut Value::Thunk(idx) = self { - // *self = env.lookup_cache(idx, |env| engine.eval_thunk(idx, env))? *self = env.lookup_cache(idx, |_| unreachable!())? } Ok(self) diff --git a/src/ty/internal/string.rs b/src/ty/internal/string.rs index 25a61dc..34c8c12 100644 --- a/src/ty/internal/string.rs +++ b/src/ty/internal/string.rs @@ -1,7 +1,5 @@ // TODO: Contextful String -use ecow::EcoString; - pub struct StringContext { context: Vec<()>, } @@ -15,12 +13,12 @@ impl StringContext { } pub struct ContextfulString { - string: EcoString, + string: String, context: StringContext, } impl ContextfulString { - pub fn new(string: EcoString) -> ContextfulString { + pub fn new(string: String) -> ContextfulString { ContextfulString { string, context: StringContext::new(), diff --git a/src/ty/public.rs b/src/ty/public.rs index 0e60a4e..a16795c 100644 --- a/src/ty/public.rs +++ b/src/ty/public.rs @@ -4,15 +4,14 @@ use std::ops::Deref; use std::sync::LazyLock; use derive_more::{Constructor, IsVariant, Unwrap}; -use ecow::EcoString; use regex::Regex; use super::common::*; #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)] -pub struct Symbol(EcoString); +pub struct Symbol(String); -impl> From for Symbol { +impl> From for Symbol { fn from(value: T) -> Self { Symbol(value.into()) } @@ -44,11 +43,11 @@ impl Deref for Symbol { } impl Symbol { - pub fn into_inner(self) -> EcoString { + pub fn into_inner(self) -> String { self.0 } - pub fn as_inner(&self) -> &EcoString { + pub fn as_inner(&self) -> &String { &self.0 } } @@ -108,7 +107,7 @@ impl Display for List { #[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)] pub enum Value { Const(Const), - String(EcoString), + String(String), AttrSet(AttrSet), List(List), Catchable(Catchable),