From 1c328f50810b751e39f72e4c18917278336e236d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Wed, 5 Aug 2026 17:52:42 +0800 Subject: [PATCH] =?UTF-8?q?refactor(compiler):=20=E5=8E=9F=E5=AD=90?= =?UTF-8?q?=E5=88=87=E6=8D=A2=20emit=20=E4=BA=A7=E5=87=BA=E7=AC=A6?= =?UTF-8?q?=E5=8F=B7=E5=8C=96=20IR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_compiler/src/class/emit.rs | 86 ++--- crates/oxide_compiler/src/class/field.rs | 5 +- crates/oxide_compiler/src/class/method.rs | 28 +- crates/oxide_compiler/src/class/prototype.rs | 28 +- crates/oxide_compiler/src/compiler.rs | 361 +++++------------- crates/oxide_compiler/src/emit_ctx.rs | 24 +- crates/oxide_compiler/src/expr/assignment.rs | 105 +++-- crates/oxide_compiler/src/expr/call.rs | 63 +-- .../oxide_compiler/src/expr/function/mod.rs | 23 +- crates/oxide_compiler/src/expr/identifier.rs | 12 +- crates/oxide_compiler/src/expr/literal.rs | 18 +- crates/oxide_compiler/src/expr/member/mod.rs | 38 +- .../oxide_compiler/src/expr/object/array.rs | 14 +- .../src/expr/object/object_literal.rs | 26 +- .../oxide_compiler/src/expr/operator/mod.rs | 159 ++++---- .../src/expr/template/tagged.rs | 42 +- .../src/expr/template/template_literal.rs | 23 +- crates/oxide_compiler/src/expr/this.rs | 7 +- crates/oxide_compiler/src/ir/inst.rs | 30 ++ crates/oxide_compiler/src/shared/binding.rs | 96 +++-- crates/oxide_compiler/src/shared/chain.rs | 95 +++-- .../oxide_compiler/src/shared/class_field.rs | 18 +- crates/oxide_compiler/src/shared/class_key.rs | 8 +- .../src/stmt/basic/break_continue.rs | 5 +- .../oxide_compiler/src/stmt/basic/labeled.rs | 6 +- .../src/stmt/basic/return_stmt.rs | 8 +- crates/oxide_compiler/src/stmt/control.rs | 27 +- .../src/stmt/declaration/class_decl.rs | 6 +- .../src/stmt/declaration/function_decl.rs | 11 +- .../src/stmt/declaration/var.rs | 16 +- .../src/stmt/exception/throw.rs | 6 +- .../src/stmt/exception/try_catch.rs | 39 +- .../src/stmt/iteration/do_while.rs | 14 +- .../oxide_compiler/src/stmt/iteration/for_.rs | 31 +- .../src/stmt/iteration/for_in.rs | 36 +- .../src/stmt/iteration/for_of.rs | 29 +- .../src/stmt/iteration/while_loop.rs | 16 +- crates/oxide_compiler/src/stmt/switch.rs | 27 +- 38 files changed, 720 insertions(+), 866 deletions(-) diff --git a/crates/oxide_compiler/src/class/emit.rs b/crates/oxide_compiler/src/class/emit.rs index 5b918f6..5bf0d0c 100644 --- a/crates/oxide_compiler/src/class/emit.rs +++ b/crates/oxide_compiler/src/class/emit.rs @@ -1,5 +1,7 @@ use crate::compiler::{CompileCtx, Compiler, FunctionBodyContext}; -use oxide_bytecode::opcode::{self, OpCode}; +use crate::ir::inst::Inst; +use crate::ir::operand::Operand; +use oxide_bytecode::opcode::OpCode; use oxide_parser::{Class, ClassElement, MethodDefinitionKind, PropertyKey}; impl Compiler { @@ -57,6 +59,17 @@ impl Compiler { ctx.in_derived_constructor = is_derived; ctx.scopes.private_name_map = private_names.clone(); + let emit_instance_fields = |compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> { + for field in &instance_fields { + if let PropertyKey::PrivateIdentifier(private) = &field.key { + compiler.emit_private_field_init(Operand::This, private.name.as_str(), field.value.as_ref(), field_ctx)?; + } else { + compiler.emit_public_field_init(Operand::This, &field.key, field.computed, field.value.as_ref(), field_ctx)?; + } + } + Ok(()) + }; + let mut ctor_module = if let Some(method) = constructor_method { let (param_names, body_stmts) = self.extract_function_parts(method.value.as_ref())?; self.compile_function_body_with_field_hooks( @@ -66,27 +79,7 @@ impl Compiler { false, &self_binding, FunctionBodyContext::ClassElement, - Some(|compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> { - for field in &instance_fields { - if let PropertyKey::PrivateIdentifier(private) = &field.key { - compiler.emit_private_field_init( - 254, - private.name.as_str(), - field.value.as_ref(), - field_ctx, - )?; - } else { - compiler.emit_public_field_init( - 254, - &field.key, - field.computed, - field.value.as_ref(), - field_ctx, - )?; - } - } - Ok(()) - }), + Some(&emit_instance_fields), is_derived, )? } else { @@ -97,54 +90,21 @@ impl Compiler { false, &self_binding, FunctionBodyContext::ClassElement, - Some(|compiler: &Compiler, field_ctx: &mut CompileCtx| -> Result<(), String> { - for field in &instance_fields { - if let PropertyKey::PrivateIdentifier(private) = &field.key { - compiler.emit_private_field_init( - 254, - private.name.as_str(), - field.value.as_ref(), - field_ctx, - )?; - } else { - compiler.emit_public_field_init( - 254, - &field.key, - field.computed, - field.value.as_ref(), - field_ctx, - )?; - } - } - Ok(()) - }), + Some(&emit_instance_fields), is_derived, )?; if is_derived { - module.bytecode.clear(); + module.insts.clear(); module.constants.clear(); module.n_registers = 1; - module.bytecode.push(opcode::encode(OpCode::SUPER_CALL, 0, 0, 0)); - module.bytecode.push(0); + module.insts.push(Inst::super_call(Operand::None, Operand::None, 0)); let mut field_ctx = CompileCtx::new(); field_ctx.scopes.private_name_map = private_names.clone(); - for field in &instance_fields { - if let PropertyKey::PrivateIdentifier(private) = &field.key { - self.emit_private_field_init(254, private.name.as_str(), field.value.as_ref(), &mut field_ctx)?; - } else { - self.emit_public_field_init( - 254, - &field.key, - field.computed, - field.value.as_ref(), - &mut field_ctx, - )?; - } - } - module.bytecode.extend(field_ctx.bytecode); + emit_instance_fields(self, &mut field_ctx)?; + module.insts.extend(field_ctx.insts); module.constants = field_ctx.constants; module.n_registers = field_ctx.max_regs.max(1); - module.bytecode.push(opcode::encode(OpCode::RETURN, 0, 0, 0)); + module.insts.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); } module }; @@ -152,9 +112,9 @@ impl Compiler { ctor_module.is_class_constructor = true; ctor_module.is_derived_constructor = is_derived; ctor_module.function_name = ctor_name.clone(); - ctx.sub_modules.push(ctor_module); + ctx.nested.push(ctor_module); - self.emit_class_prototype(ctor_reg, proto_reg, super_reg, ctx.sub_modules.len() as u32, ctx)?; + self.emit_class_prototype(ctor_reg, proto_reg, super_reg, ctx.nested.len() as u16, ctx)?; self.emit_class_methods(&class.body.body, ctor_reg, proto_reg, &self_binding, ctx)?; self.emit_class_static_elements(&class.body.body, ctor_reg, ctx)?; diff --git a/crates/oxide_compiler/src/class/field.rs b/crates/oxide_compiler/src/class/field.rs index ad0715b..ec3a935 100644 --- a/crates/oxide_compiler/src/class/field.rs +++ b/crates/oxide_compiler/src/class/field.rs @@ -1,4 +1,5 @@ use crate::compiler::{CompileCtx, Compiler}; +use crate::ir::operand::Operand; use oxide_parser::{ClassElement, PropertyKey}; impl Compiler { @@ -17,9 +18,9 @@ impl Compiler { let prop = prop.as_ref(); if prop.r#static { if let PropertyKey::PrivateIdentifier(private) = &prop.key { - self.emit_private_field_init(ctor_reg, private.name.as_str(), prop.value.as_ref(), ctx)?; + self.emit_private_field_init(Operand::Reg(ctor_reg as u32), private.name.as_str(), prop.value.as_ref(), ctx)?; } else { - self.emit_public_field_init(ctor_reg, &prop.key, prop.computed, prop.value.as_ref(), ctx)?; + self.emit_public_field_init(Operand::Reg(ctor_reg as u32), &prop.key, prop.computed, prop.value.as_ref(), ctx)?; } } } diff --git a/crates/oxide_compiler/src/class/method.rs b/crates/oxide_compiler/src/class/method.rs index 274e255..1b7c760 100644 --- a/crates/oxide_compiler/src/class/method.rs +++ b/crates/oxide_compiler/src/class/method.rs @@ -1,6 +1,8 @@ use crate::compiler::{CompileCtx, Compiler, FunctionBodyContext}; +use crate::ir::inst::Inst; +use crate::ir::operand::Operand; use oxide_bytecode::module::Constant; -use oxide_bytecode::opcode::{self, OpCode}; +use oxide_bytecode::opcode::OpCode; use oxide_parser::{ClassElement, MethodDefinitionKind, PropertyKey}; impl Compiler { @@ -17,7 +19,7 @@ impl Compiler { } if matches!(method.key, PropertyKey::PrivateIdentifier(_)) { let home_reg = if method.r#static { ctor_reg } else { proto_reg }; - self.emit_private_method_init(home_reg, method, home_reg, ctx)?; + self.emit_private_method_init(Operand::Reg(home_reg as u32), method, Operand::Reg(home_reg as u32), ctx)?; continue; } let home_reg = if method.r#static { ctor_reg } else { proto_reg }; @@ -28,13 +30,13 @@ impl Compiler { self.class_property_name(&method.key)? }; let accessor_reg = - self.emit_class_method_function(method, &method_name, home_reg, ctx, self_binding)?; + self.emit_class_method_function(method, &method_name, Operand::Reg(home_reg as u32), ctx, self_binding)?; match method.kind { MethodDefinitionKind::Method => { if method.computed { - ctx.emit(opcode::encode(OpCode::SET_PROP_DYNAMIC, home_reg, key_reg, accessor_reg)); + ctx.inst(Inst::new(OpCode::SET_PROP_DYNAMIC, Operand::Reg(home_reg as u32), Operand::Reg(key_reg as u32), Operand::Reg(accessor_reg as u32))); } else { - ctx.emit(opcode::encode(OpCode::SET_PROP, home_reg, accessor_reg, key_reg)); + ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(home_reg as u32), Operand::Reg(accessor_reg as u32), Operand::Reg(key_reg as u32))); } } MethodDefinitionKind::Get | MethodDefinitionKind::Set => { @@ -49,8 +51,12 @@ impl Compiler { }; let key_name = self.class_property_name(&method.key)?; let key_idx = ctx.add_constant(Constant::String(key_name)); - ctx.emit(opcode::encode(OpCode::DEFINE_ACCESSOR, home_reg, get_reg, set_reg)); - ctx.emit(key_idx as u32); + ctx.inst(Inst::define_accessor( + Operand::Reg(home_reg as u32), + Operand::Reg(get_reg as u32), + Operand::Reg(set_reg as u32), + key_idx as u32, + )); } MethodDefinitionKind::Constructor => continue, } @@ -62,7 +68,7 @@ impl Compiler { } pub(crate) fn emit_class_method_function( - &self, method: &oxide_parser::MethodDefinition, method_name: &str, home_reg: u8, ctx: &mut CompileCtx, + &self, method: &oxide_parser::MethodDefinition, method_name: &str, home_reg: Operand, ctx: &mut CompileCtx, self_binding: &[(&str, u8)], ) -> Result { let (param_names, body_stmts) = self.extract_function_parts(method.value.as_ref())?; @@ -82,10 +88,10 @@ impl Compiler { ctx.in_static_method = saved_static; method_module.function_name = Some(method_name.to_string()); method_module.needs_home_object = true; - ctx.sub_modules.push(method_module); + ctx.nested.push(method_module); let method_reg = ctx.alloc_reg(); - ctx.emit_create_closure(method_reg, ctx.sub_modules.len() as u32); - ctx.emit(opcode::encode(OpCode::SET_HOME_OBJECT, method_reg, home_reg, 0)); + ctx.inst(Inst::create_closure(Operand::Reg(method_reg as u32), ctx.nested.len() as u16)); + ctx.inst(Inst::new(OpCode::SET_HOME_OBJECT, Operand::Reg(method_reg as u32), home_reg, Operand::None)); Ok(method_reg) } } diff --git a/crates/oxide_compiler/src/class/prototype.rs b/crates/oxide_compiler/src/class/prototype.rs index 0024d3a..86c513e 100644 --- a/crates/oxide_compiler/src/class/prototype.rs +++ b/crates/oxide_compiler/src/class/prototype.rs @@ -1,33 +1,35 @@ use crate::compiler::{CompileCtx, Compiler}; +use crate::ir::inst::Inst; +use crate::ir::operand::Operand; use oxide_bytecode::module::Constant; -use oxide_bytecode::opcode::{self, OpCode}; +use oxide_bytecode::opcode::OpCode; impl Compiler { pub(crate) fn emit_class_prototype( - &self, ctor_reg: u8, proto_reg: u8, super_reg: Option, sub_idx: u32, ctx: &mut CompileCtx, + &self, ctor_reg: u8, proto_reg: u8, super_reg: Option, sub_idx: u16, ctx: &mut CompileCtx, ) -> Result<(), String> { - ctx.emit_create_closure(ctor_reg, sub_idx); - ctx.emit(opcode::encode(OpCode::NEW_OBJECT, proto_reg, 0, 0)); + ctx.inst(Inst::create_closure(Operand::Reg(ctor_reg as u32), sub_idx)); + ctx.inst(Inst::new(OpCode::NEW_OBJECT, Operand::Reg(proto_reg as u32), Operand::None, Operand::None)); if let Some(super_reg) = super_reg { let proto_key_idx = ctx.add_constant(Constant::String("prototype".to_string())); let parent_proto_key_reg = ctx.alloc_reg(); - ctx.emit_load_const(parent_proto_key_reg, proto_key_idx); + ctx.inst(Inst::load_const(Operand::Reg(parent_proto_key_reg as u32), proto_key_idx)); let parent_proto_reg = ctx.alloc_reg(); - ctx.emit(opcode::encode(OpCode::GET_PROP, super_reg, parent_proto_reg, parent_proto_key_reg)); + ctx.inst(Inst::new(OpCode::GET_PROP, Operand::Reg(super_reg as u32), Operand::Reg(parent_proto_reg as u32), Operand::Reg(parent_proto_key_reg as u32))); let proto_link_idx = ctx.add_constant(Constant::String("__proto__".to_string())); let proto_link_key_reg = ctx.alloc_reg(); - ctx.emit_load_const(proto_link_key_reg, proto_link_idx); - ctx.emit(opcode::encode(OpCode::SET_PROP, proto_reg, parent_proto_reg, proto_link_key_reg)); - ctx.emit(opcode::encode(OpCode::SET_PROP, ctor_reg, super_reg, proto_link_key_reg)); + ctx.inst(Inst::load_const(Operand::Reg(proto_link_key_reg as u32), proto_link_idx)); + ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(proto_reg as u32), Operand::Reg(parent_proto_reg as u32), Operand::Reg(proto_link_key_reg as u32))); + ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(ctor_reg as u32), Operand::Reg(super_reg as u32), Operand::Reg(proto_link_key_reg as u32))); } let ctor_key_idx = ctx.add_constant(Constant::String("constructor".to_string())); let ctor_key_reg = ctx.alloc_reg(); - ctx.emit_load_const(ctor_key_reg, ctor_key_idx); - ctx.emit(opcode::encode(OpCode::SET_PROP, proto_reg, ctor_reg, ctor_key_reg)); + ctx.inst(Inst::load_const(Operand::Reg(ctor_key_reg as u32), ctor_key_idx)); + ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(proto_reg as u32), Operand::Reg(ctor_reg as u32), Operand::Reg(ctor_key_reg as u32))); let proto_key_idx = ctx.add_constant(Constant::String("prototype".to_string())); let proto_key_reg = ctx.alloc_reg(); - ctx.emit_load_const(proto_key_reg, proto_key_idx); - ctx.emit(opcode::encode(OpCode::SET_PROP, ctor_reg, proto_reg, proto_key_reg)); + ctx.inst(Inst::load_const(Operand::Reg(proto_key_reg as u32), proto_key_idx)); + ctx.inst(Inst::new(OpCode::SET_PROP, Operand::Reg(ctor_reg as u32), Operand::Reg(proto_reg as u32), Operand::Reg(proto_key_reg as u32))); Ok(()) } } diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index db38beb..a1ef240 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -2,10 +2,14 @@ use std::cell::Cell; use std::collections::HashMap; use crate::emit_ctx::{LabelCtx, ScopeCtx}; +use crate::ir::inst::Inst; +use crate::ir::lower::lower; +use crate::ir::operand::{LabelId, Operand}; +use crate::ir::IRFunction; use crate::symbol_table::{Binding, SymbolTable}; use oxide_bytecode::module::CompiledModule; use oxide_bytecode::module::UpvalueCapture; -use oxide_bytecode::opcode::{self, OpCode}; +use oxide_bytecode::opcode::OpCode; pub use crate::hash::{compiled_module_hash, structural_hash}; use crate::symbol_table::ScopeKind; @@ -105,43 +109,9 @@ const BUILTIN_GLOBALS: &[&str] = &[ "decodeURIComponent", ]; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[allow(dead_code)] -pub(crate) enum Label { - IfElse(u32), - IfEnd(u32), - WhileStart(u32), - WhileEnd(u32), - ForStart(u32), - ForUpdate(u32), - ForEnd(u32), - TernaryEnd(u32), - TernaryElse(u32), - DoWhileStart(u32), - DoWhileEnd(u32), - ForInStart(u32), - ForInEnd(u32), - ForOfStart(u32), - ForOfEnd(u32), - SwitchEnd(u32), - SwitchCase(u32, u32), - CatchBody(u32), - FinallyBody(u32), - TryEnd(u32), - LabeledEnd(u32), -} - -pub(crate) struct JumpFixup { - pub(crate) pc: usize, - pub(crate) label: Label, - pub(crate) opcode: OpCode, - pub(crate) rd: u8, -} - pub(crate) struct FieldBuffer { - pub(crate) bytecode: Vec, - pub(crate) labels: Vec<(Label, usize)>, - pub(crate) fixups: Vec, + pub(crate) insts: Vec, + pub(crate) labels: Vec<(LabelId, usize)>, } /// A labeled-statement scope active during emission. `break label` targets @@ -150,13 +120,12 @@ pub(crate) struct FieldBuffer { #[derive(Debug, Clone)] pub(crate) struct LabelScope { pub(crate) name: String, - pub(crate) break_label: Label, - pub(crate) continue_label: Option