refactor: use GAT in enum Ir

This commit is contained in:
2026-05-01 20:18:00 +08:00
parent 0df38f374f
commit 7a7a9c3735
7 changed files with 331 additions and 214 deletions
+109 -36
View File
@@ -1,6 +1,4 @@
use std::ops::Deref;
use fix_builtins::BuiltinId;
use fix_builtins::{BUILTINS, BuiltinId};
use fix_common::StringId;
use fix_ir::{Attr, BinOpKind, Ir, MaybeThunk, Param, RawIrRef, ThunkId, UnOpKind};
use hashbrown::HashMap;
@@ -116,9 +114,12 @@ struct BytecodeEmitter<'a, Ctx: BytecodeContext> {
#[derive(Debug, Clone, Copy, TryFromPrimitive)]
pub enum OperandType {
Const,
BigInt,
Local,
Builtins,
BigInt,
ReplBinding,
ScopedImportBinding,
WithLookup,
}
pub enum Const {
@@ -126,6 +127,7 @@ pub enum Const {
Float(f64),
Bool(bool),
String(StringId),
Path(StringId),
PrimOp {
id: BuiltinId,
arity: u8,
@@ -143,9 +145,12 @@ pub enum AttrKeyType {
pub enum InlineOperand {
Const(Const),
BigInt(i64),
Local { layer: u8, local: u32 },
Builtins,
BigInt(i64),
ReplBinding(StringId),
ScopedImportBinding(StringId),
WithLookup(StringId),
}
pub fn compile_bytecode(ir: RawIrRef<'_>, ctx: &mut impl BytecodeContext) -> InstructionPtr {
@@ -164,9 +169,9 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
#[must_use]
fn inline_maybe_thunk(&self, val: MaybeThunk) -> InlineOperand {
fn inline_maybe_thunk(&self, val: &MaybeThunk) -> InlineOperand {
use MaybeThunk::*;
match val {
match *val {
Int(x) => {
if let Ok(x) = x.try_into() {
InlineOperand::Const(Const::Smi(x))
@@ -178,37 +183,55 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
Bool(b) => InlineOperand::Const(Const::Bool(b)),
Null => InlineOperand::Const(Const::Null),
Str(id) => InlineOperand::Const(Const::String(id)),
Path(id) => InlineOperand::Const(Const::String(id)),
Thunk(id) => {
let (layer, local) = self.resolve_thunk(id);
InlineOperand::Local { layer, local }
}
Arg { layer } => InlineOperand::Local {
layer,
local: 0,
Arg { layer } => InlineOperand::Local { layer, local: 0 },
Builtin(id) => {
let (_, arity) = BUILTINS[id as usize];
InlineOperand::Const(Const::PrimOp { id, arity, dispatch_ip: id.entry_phase().ip() })
},
_ => todo!(),
Builtins => InlineOperand::Builtins,
ReplBinding(id) => InlineOperand::ReplBinding(id),
ScopedImportBinding(id) => InlineOperand::ScopedImportBinding(id),
WithLookup(id) => InlineOperand::WithLookup(id),
}
}
fn emit_maybe_thunk(&mut self, val: MaybeThunk) {
fn emit_maybe_thunk(&mut self, val: &MaybeThunk) {
use InlineOperand::*;
let operand = self.inline_maybe_thunk(val);
match operand {
InlineOperand::Const(val) => {
Const(val) => {
let idx = self.ctx.add_constant(val);
self.emit_u8(OperandType::Const as u8);
self.emit_u32(idx);
}
InlineOperand::Local { layer, local } => {
BigInt(val) => {
self.emit_u8(OperandType::BigInt as u8);
self.emit_i64(val);
}
Local { layer, local } => {
self.emit_u8(OperandType::Local as u8);
self.emit_u8(layer);
self.emit_u32(local);
}
InlineOperand::Builtins => {
Builtins => {
self.emit_u8(OperandType::Builtins as u8);
}
InlineOperand::BigInt(val) => {
self.emit_u8(OperandType::BigInt as u8);
self.emit_i64(val);
ReplBinding(id) => {
self.emit_u8(OperandType::ReplBinding as u8);
self.emit_str_id(id);
}
ScopedImportBinding(id) => {
self.emit_u8(OperandType::ScopedImportBinding as u8);
self.emit_str_id(id);
}
WithLookup(id) => {
self.emit_u8(OperandType::WithLookup as u8);
self.emit_str_id(id);
}
}
}
@@ -315,7 +338,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
fn count_with_thunks(&self, ir: RawIrRef<'_>) -> usize {
match ir.deref() {
match ir {
Ir::With { thunks, body, .. } => thunks.len() + self.count_with_thunks(*body),
Ir::TopLevel { thunks, body } => thunks.len() + self.count_with_thunks(*body),
Ir::If { cond, consq, alter } => {
@@ -361,7 +384,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
ir: RawIrRef<'ir>,
out: &mut Vec<(ThunkId, RawIrRef<'ir>)>,
) {
match ir.deref() {
match ir {
Ir::With { thunks, body, .. } => {
for &(id, inner) in thunks.iter() {
out.push((id, inner));
@@ -422,7 +445,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
fn emit_toplevel(&mut self, ir: RawIrRef<'_>) {
match ir.deref() {
match ir {
&Ir::TopLevel { body, ref thunks } => {
let with_thunk_count = self.count_with_thunks(body);
let total_slots = thunks.len() + with_thunk_count;
@@ -468,11 +491,11 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
fn emit_expr(&mut self, ir: RawIrRef<'_>) {
match ir.deref() {
match ir {
&Ir::Int(x) => {
if x <= i32::MAX as i64 {
if let Ok(x) = x.try_into() {
self.emit_op(Op::PushSmi);
self.emit_i32(x as i32);
self.emit_i32(x);
} else {
self.emit_op(Op::PushBigInt);
self.emit_i64(x);
@@ -567,10 +590,6 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
} => {
self.emit_select(expr, attrpath, default, span);
}
&Ir::Thunk(id) => {
let (layer, local) = self.resolve_thunk(id);
self.emit_load(layer, local);
}
Ir::Builtins => {
self.emit_op(Op::LoadBuiltins);
}
@@ -580,7 +599,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
&Ir::BuiltinConst(id) => {
self.emit_select(
RawIrRef(&Ir::Builtins),
&Ir::Builtins,
&[Attr::Str(id, TextRange::default())],
None,
TextRange::default(),
@@ -642,6 +661,60 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
self.emit_op(Op::LookupWith);
self.emit_str_id(name);
}
&Ir::MaybeThunk(thunk) => {
use MaybeThunk::*;
match *thunk {
Int(x) => {
if let Ok(x) = x.try_into() {
self.emit_op(Op::PushSmi);
self.emit_i32(x);
} else {
self.emit_op(Op::PushBigInt);
self.emit_i64(x);
}
}
Float(x) => {
self.emit_op(Op::PushFloat);
self.emit_f64(x);
}
Bool(true) => self.emit_op(Op::PushTrue),
Bool(false) => self.emit_op(Op::PushFalse),
Null => self.emit_op(Op::PushNull),
Str(id) => {
self.emit_op(Op::PushString);
self.emit_str_id(id);
}
Path(id) => {
self.emit_op(Op::PushString);
self.emit_str_id(id);
self.emit_op(Op::ResolvePath);
}
Thunk(id) => {
let (layer, local) = self.resolve_thunk(id);
self.emit_load(layer, local);
}
Arg { layer } => self.emit_load(layer, 0),
Builtin(id) => {
self.emit_op(Op::LoadBuiltin);
self.emit_u8(id as u8);
}
Builtins => self.emit_op(Op::LoadBuiltins),
ReplBinding(name) => {
self.emit_op(Op::LoadReplBinding);
self.emit_str_id(name);
}
ScopedImportBinding(name) => {
self.emit_op(Op::LoadScopedBinding);
self.emit_str_id(name);
}
WithLookup(name) => {
// TODO: specialize shallow with lookups
self.emit_op(Op::PrepareWith);
self.emit_op(Op::LookupWith);
self.emit_str_id(name);
}
}
}
}
}
@@ -739,11 +812,11 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
}
}
fn emit_func(
fn emit_func<'ir>(
&mut self,
thunks: &[(ThunkId, RawIrRef<'_>)],
param: &Option<Param<'_>>,
body: RawIrRef<'_>,
thunks: &[(ThunkId, RawIrRef<'ir>)],
param: &Option<Param<'ir>>,
body: RawIrRef<'ir>,
) {
let with_thunk_count = self.count_with_thunks(body);
let total_slots = thunks.len() + with_thunk_count;
@@ -793,8 +866,8 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
fn emit_attrset(
&mut self,
stcs: &fix_ir::HashMap<'_, StringId, (MaybeThunk, TextRange)>,
dyns: &[(RawIrRef<'_>, MaybeThunk, TextRange)],
stcs: &fix_ir::HashMap<'_, StringId, (&MaybeThunk, TextRange)>,
dyns: &[(RawIrRef<'_>, &MaybeThunk, TextRange)],
) {
if stcs.is_empty() && dyns.is_empty() {
self.emit_op(Op::MakeEmptyAttrs);
@@ -913,7 +986,7 @@ impl<'a, Ctx: BytecodeContext> BytecodeEmitter<'a, Ctx> {
fn emit_with(
&mut self,
namespace: MaybeThunk,
namespace: &MaybeThunk,
body: RawIrRef<'_>,
thunks: &[(ThunkId, RawIrRef<'_>)],
) {