rewrite VM to support reentry (WIP)

This commit is contained in:
2026-03-22 16:41:13 +08:00
parent b3f1f4f6ff
commit 6567ed4058
18 changed files with 1728 additions and 3315 deletions
+149 -75
View File
@@ -7,8 +7,10 @@ use rnix::TextRange;
use string_interner::Symbol as _;
use crate::ir::{ArgId, Attr, BinOpKind, Ir, Param, RawIrRef, StringId, ThunkId, UnOpKind};
use crate::runtime::{BUILTINS, BuiltinId};
use crate::runtime::value::{Null, PrimOp, StaticValue};
pub(crate) struct InstructionPtr(pub usize);
pub struct InstructionPtr(pub(crate) usize);
#[derive(Collect)]
#[collect(require_static)]
@@ -22,6 +24,7 @@ pub(crate) trait BytecodeContext {
fn register_span(&mut self, range: TextRange) -> u32;
fn get_code(&self) -> &[u8];
fn get_code_mut(&mut self) -> &mut Vec<u8>;
fn add_constant(&mut self, val: crate::runtime::value::StaticValue) -> u32;
}
#[repr(u8)]
@@ -49,13 +52,13 @@ pub enum Op {
CallNoSpan,
MakeAttrs,
MakeAttrsDyn,
MakeEmptyAttrs,
Select,
SelectDefault,
HasAttr,
MakeList,
MakeEmptyList,
OpAdd,
OpSub,
@@ -73,7 +76,6 @@ pub enum Op {
OpNeg,
OpNot,
ForceBool,
JumpIfFalse,
JumpIfTrue,
Jump,
@@ -109,6 +111,21 @@ struct BytecodeEmitter<'a, Ctx: BytecodeContext> {
scope_stack: Vec<ScopeInfo>,
}
pub(crate) const OPERAND_CONST: u8 = 0;
pub(crate) const OPERAND_LOCAL: u8 = 1;
pub(crate) const OPERAND_BUILTINS: u8 = 2;
pub(crate) const OPERAND_BIGINT: u8 = 3;
pub(crate) const KEY_STATIC: u8 = 0;
pub(crate) const KEY_DYNAMIC: u8 = 1;
enum InlineOperand {
Const(StaticValue),
Local { layer: u16, local: u32 },
Builtins,
BigInt(i64),
}
pub(crate) fn compile_bytecode(ir: RawIrRef<'_>, ctx: &mut impl BytecodeContext) -> InstructionPtr {
let ip = ctx.get_code().len();
let mut emitter = BytecodeEmitter::new(ctx);
@@ -124,6 +141,61 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
}
fn classify_value(&mut self, ir: RawIrRef<'_>) -> InlineOperand {
match ir.deref() {
&Ir::Int(x) => {
if x <= i32::MAX as i64 {
InlineOperand::Const(StaticValue::new_inline(x as i32))
} else {
InlineOperand::BigInt(x)
}
}
&Ir::Float(x) => InlineOperand::Const(StaticValue::new_float(x)),
&Ir::Bool(b) => InlineOperand::Const(StaticValue::new_inline(b)),
Ir::Null => InlineOperand::Const(StaticValue::new_inline(Null)),
Ir::Str(s) => {
let sid = self.ctx.intern_string(s.deref());
InlineOperand::Const(StaticValue::new_inline(sid))
}
&Ir::Thunk(id) => {
let (layer, local) = self.resolve_thunk(id);
InlineOperand::Local { layer, local }
}
&Ir::Arg(id) => {
let (layer, local) = self.resolve_arg(id);
InlineOperand::Local { layer, local }
}
&Ir::Builtin(id) => {
let arity = BUILTINS[id as usize].1;
InlineOperand::Const(StaticValue::new_inline(PrimOp { id, arity }))
}
Ir::Builtins => InlineOperand::Builtins,
_ => panic!("cannot classify IR node as inline operand"),
}
}
fn emit_inline_operand(&mut self, operand: InlineOperand) {
match operand {
InlineOperand::Const(val) => {
let idx = self.ctx.add_constant(val);
self.emit_u8(OPERAND_CONST);
self.emit_u32(idx);
}
InlineOperand::Local { layer, local } => {
self.emit_u8(OPERAND_LOCAL);
self.emit_u8(layer as u8);
self.emit_u32(local);
}
InlineOperand::Builtins => {
self.emit_u8(OPERAND_BUILTINS);
}
InlineOperand::BigInt(val) => {
self.emit_u8(OPERAND_BIGINT);
self.emit_i64(val);
}
}
}
#[inline]
fn emit_op(&mut self, op: Op) {
self.ctx.get_code_mut().push(op as u8);
@@ -382,11 +454,11 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
fn emit_toplevel(&mut self, ir: RawIrRef<'_>) {
match ir.deref() {
Ir::TopLevel { body, thunks } => {
let with_thunk_count = self.count_with_thunks(*body);
&Ir::TopLevel { body, ref thunks } => {
let with_thunk_count = self.count_with_thunks(body);
let total_slots = thunks.len() + with_thunk_count;
let all_thunks = self.collect_all_thunks(thunks, *body);
let all_thunks = self.collect_all_thunks(thunks, body);
let thunk_ids: Vec<ThunkId> = all_thunks.iter().map(|&(id, _)| id).collect();
self.push_scope(false, None, &thunk_ids);
@@ -397,7 +469,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
self.emit_scope_thunks(thunks);
self.emit_expr(*body);
self.emit_expr(body);
self.emit_op(Op::Return);
self.pop_scope();
@@ -459,7 +531,6 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
&Ir::If { cond, consq, alter } => {
self.emit_expr(cond);
self.emit_op(Op::ForceBool);
self.emit_op(Op::JumpIfFalse);
let else_placeholder = self.emit_i32_placeholder();
@@ -504,15 +575,20 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.emit_attrset(stcs, dyns);
}
Ir::List { items } => {
for &item in items.iter() {
self.emit_expr(item);
if items.is_empty() {
self.emit_op(Op::MakeEmptyList);
} else {
self.emit_op(Op::MakeList);
self.emit_u32(items.len() as u32);
for &item in items.iter() {
let operand = self.classify_value(item);
self.emit_inline_operand(operand);
}
}
self.emit_op(Op::MakeList);
self.emit_u32(items.len() as u32);
}
&Ir::Call { func, arg, span } => {
self.emit_expr(func);
self.emit_expr(arg);
self.emit_expr(func);
let span_id = self.ctx.register_span(span);
self.emit_op(Op::Call);
self.emit_u32(span_id);
@@ -539,20 +615,29 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
Ir::Builtins => {
self.emit_op(Op::LoadBuiltins);
}
&Ir::Builtin(name) => {
&Ir::Builtin(id) => {
self.emit_op(Op::LoadBuiltin);
self.emit_u32(name.0.to_usize() as u32);
self.emit_u8(id as u8);
}
&Ir::BuiltinConst(id) => {
self.emit_select(
RawIrRef(&Ir::Builtins),
&[Attr::Str(id, TextRange::default())],
None,
TextRange::default(),
);
}
&Ir::ConcatStrings {
ref parts,
force_string,
} => {
for &part in parts.iter() {
self.emit_expr(part);
}
self.emit_op(Op::ConcatStrings);
self.emit_u16(parts.len() as u16);
self.emit_u8(if force_string { 1 } else { 0 });
for &part in parts.iter() {
let operand = self.classify_value(part);
self.emit_inline_operand(operand);
}
}
&Ir::HasAttr { lhs, ref rhs } => {
self.emit_has_attr(lhs, rhs);
@@ -603,13 +688,11 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
match kind {
And => {
self.emit_expr(lhs);
self.emit_op(Op::ForceBool);
self.emit_op(Op::JumpIfFalse);
let skip_placeholder = self.emit_i32_placeholder();
let after_jif = self.ctx.get_code_mut().len();
self.emit_expr(rhs);
self.emit_op(Op::ForceBool);
self.emit_op(Op::Jump);
let end_placeholder = self.emit_i32_placeholder();
let after_jump = self.ctx.get_code_mut().len();
@@ -624,13 +707,11 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
Or => {
self.emit_expr(lhs);
self.emit_op(Op::ForceBool);
self.emit_op(Op::JumpIfTrue);
let skip_placeholder = self.emit_i32_placeholder();
let after_jit = self.ctx.get_code_mut().len();
self.emit_expr(rhs);
self.emit_op(Op::ForceBool);
self.emit_op(Op::Jump);
let end_placeholder = self.emit_i32_placeholder();
let after_jump = self.ctx.get_code_mut().len();
@@ -645,13 +726,11 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
Impl => {
self.emit_expr(lhs);
self.emit_op(Op::ForceBool);
self.emit_op(Op::JumpIfFalse);
let skip_placeholder = self.emit_i32_placeholder();
let after_jif = self.ctx.get_code_mut().len();
self.emit_expr(rhs);
self.emit_op(Op::ForceBool);
self.emit_op(Op::Jump);
let end_placeholder = self.emit_i32_placeholder();
let after_jump = self.ctx.get_code_mut().len();
@@ -759,40 +838,26 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
return;
}
if !dyns.is_empty() {
for (&sym, &(val, _)) in stcs.iter() {
self.emit_op(Op::PushString);
self.emit_str_id(sym);
self.emit_expr(val);
}
for (_, &(_, span)) in stcs.iter() {
let span_id = self.ctx.register_span(span);
self.emit_op(Op::PushSmi);
self.emit_u32(span_id);
}
for &(key, val, span) in dyns.iter() {
self.emit_expr(key);
self.emit_expr(val);
let span_id = self.ctx.register_span(span);
self.emit_op(Op::PushSmi);
self.emit_u32(span_id);
}
self.emit_op(Op::MakeAttrsDyn);
self.emit_u32(stcs.len() as u32);
self.emit_u32(dyns.len() as u32);
} else {
for (&sym, &(val, _)) in stcs.iter() {
self.emit_op(Op::PushString);
self.emit_str_id(sym);
self.emit_expr(val);
}
for (_, &(_, span)) in stcs.iter() {
let span_id = self.ctx.register_span(span);
self.emit_op(Op::PushSmi);
self.emit_u32(span_id);
}
self.emit_op(Op::MakeAttrs);
self.emit_u32(stcs.len() as u32);
let total = stcs.len() + dyns.len();
self.emit_op(Op::MakeAttrs);
self.emit_u32(total as u32);
for (&sym, &(val, span)) in stcs.iter() {
self.emit_u8(KEY_STATIC);
self.emit_str_id(sym);
let val_operand = self.classify_value(val);
self.emit_inline_operand(val_operand);
let span_id = self.ctx.register_span(span);
self.emit_u32(span_id);
}
for &(key, val, span) in dyns.iter() {
self.emit_u8(KEY_DYNAMIC);
let key_operand = self.classify_value(key);
self.emit_inline_operand(key_operand);
let val_operand = self.classify_value(val);
self.emit_inline_operand(val_operand);
let span_id = self.ctx.register_span(span);
self.emit_u32(span_id);
}
}
@@ -805,17 +870,10 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
) {
self.emit_expr(expr);
for attr in attrpath.iter() {
match *attr {
Attr::Str(sym, _) => {
self.emit_op(Op::PushString);
self.emit_str_id(sym);
}
Attr::Dynamic(expr, _) => {
self.emit_expr(expr);
}
if let Attr::Dynamic(expr, _) = *attr {
self.emit_expr(expr);
}
}
if let Some(default) = default {
self.emit_expr(default);
let span_id = self.ctx.register_span(span);
@@ -828,23 +886,39 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.emit_u16(attrpath.len() as u16);
self.emit_u32(span_id);
}
for attr in attrpath.iter() {
match *attr {
Attr::Str(sym, _) => {
self.emit_u8(KEY_STATIC);
self.emit_str_id(sym);
}
Attr::Dynamic(_, _) => {
self.emit_u8(KEY_DYNAMIC);
}
}
}
}
fn emit_has_attr(&mut self, lhs: RawIrRef<'_>, rhs: &[Attr<RawIrRef<'_>>]) {
self.emit_expr(lhs);
for attr in rhs.iter() {
match *attr {
Attr::Str(sym, _) => {
self.emit_op(Op::PushString);
self.emit_str_id(sym);
}
Attr::Dynamic(expr, _) => {
self.emit_expr(expr);
}
if let Attr::Dynamic(expr, _) = *attr {
self.emit_expr(expr);
}
}
self.emit_op(Op::HasAttr);
self.emit_u16(rhs.len() as u16);
for attr in rhs.iter() {
match *attr {
Attr::Str(sym, _) => {
self.emit_u8(KEY_STATIC);
self.emit_str_id(sym);
}
Attr::Dynamic(_, _) => {
self.emit_u8(KEY_DYNAMIC);
}
}
}
}
fn emit_with(