rewrite VM to support reentry (WIP)
This commit is contained in:
+149
-75
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user