diff --git a/crates/emitter/scripts/update_opcodes.py b/crates/emitter/scripts/update_opcodes.py index 7bc24c7d5..4d7500a53 100755 --- a/crates/emitter/scripts/update_opcodes.py +++ b/crates/emitter/scripts/update_opcodes.py @@ -389,6 +389,9 @@ def generate_emit_methods(out_f, opcodes, types): assert len(params) == 1 assert params[0][0] == 'u32' params[0] = ('GCThingIndex', params[0][1]) + elif 'JOF_JUMP' in opcode.format_: + assert params[0][0] == 'i32' + params[0] = ('BytecodeOffsetDiff', params[0][1]) else: assert int(opcode.nuses) != -1 diff --git a/crates/emitter/src/bytecode_offset.rs b/crates/emitter/src/bytecode_offset.rs new file mode 100644 index 000000000..3583ec672 --- /dev/null +++ b/crates/emitter/src/bytecode_offset.rs @@ -0,0 +1,60 @@ +/// For tracking bytecode offsets in jumps +#[derive(Clone, Copy, PartialEq, Debug)] +#[must_use] +pub struct BytecodeOffset { + pub offset: usize, +} + +impl BytecodeOffset { + fn new(offset: usize) -> Self { + Self { offset } + } + + /// diff_from is useful for finding the offset between two bytecodes. This is usually + /// used for jumps and jump_targets. + /// + /// For a forward jump, self will be a larger number, and start will be smaller + /// the output will be a positive number. for a backward jump, the reverse + /// will be true, and the number will be negative. So it is important to use this api + /// consistently in both cases. + /// + /// Examples: + /// let offset_diff: BytecodeOffsetDiff = forward_jump_target_offset.diff_from(jump) + /// let offset_diff: BytecodeOffsetDiff = backward_jump_target_offset.diff_from(jump) + pub fn diff_from(self, start: BytecodeOffset) -> BytecodeOffsetDiff { + BytecodeOffsetDiff::new(self, start) + } +} + +impl From for usize { + fn from(offset: BytecodeOffset) -> usize { + offset.offset + } +} + +impl From for BytecodeOffset { + fn from(offset: usize) -> BytecodeOffset { + BytecodeOffset::new(offset) + } +} + +pub struct BytecodeOffsetDiff { + diff: i32, +} + +impl BytecodeOffsetDiff { + fn new(end: BytecodeOffset, start: BytecodeOffset) -> Self { + let diff = (end.offset as i128 - start.offset as i128) as i32; + Self { diff } + } + + pub fn uninitialized() -> Self { + Self { diff: 0i32 } + } +} + +impl From for i32 { + fn from(offset: BytecodeOffsetDiff) -> i32 { + offset.diff + } +} diff --git a/crates/emitter/src/control_structures.rs b/crates/emitter/src/control_structures.rs index 8486852ec..970261232 100644 --- a/crates/emitter/src/control_structures.rs +++ b/crates/emitter/src/control_structures.rs @@ -1,6 +1,5 @@ -use super::emitter::BytecodeOffset; -use super::emitter::BytecodeOffsetDiff; use crate::ast_emitter::AstEmitter; +use crate::bytecode_offset::{BytecodeOffset, BytecodeOffsetDiff}; use crate::emitter::EmitError; use crate::emitter::InstructionWriter; @@ -39,7 +38,7 @@ trait Jump { // and four bytes are used in order to save memory. We are not using that // here, so instead we are using a placeholder offset set to 0, which will // be updated later in patch_and_emit_jump_target. - let placeholder_offset = BytecodeOffsetDiff{ diff: 0 }; + let placeholder_offset = BytecodeOffsetDiff::uninitialized(); match self.jump_kind() { JumpKind::Coalesce { .. } => { emitter.emit.coalesce(placeholder_offset); @@ -186,7 +185,7 @@ impl LoopControl { pub fn emit_end_target(self, emit: &mut InstructionWriter) { let offset = emit.bytecode_offset(); - let diff = BytecodeOffsetDiff::new(self.head, offset); + let diff = self.head.diff_from(offset); emit.goto_(diff); diff --git a/crates/emitter/src/emitter.rs b/crates/emitter/src/emitter.rs index 395e88759..5bf2203ca 100644 --- a/crates/emitter/src/emitter.rs +++ b/crates/emitter/src/emitter.rs @@ -5,6 +5,7 @@ // Most of this functionality isn't used yet. #![allow(dead_code)] +use crate::bytecode_offset::{BytecodeOffset, BytecodeOffsetDiff}; use crate::compilation_info::CompilationInfo; use crate::gcthings::{GCThing, GCThingIndex, GCThingList}; use crate::opcode::Opcode; @@ -99,42 +100,6 @@ pub enum SrcNoteType { #[allow(non_camel_case_types)] pub type u24 = u32; -/// For tracking bytecode offsets in jumps -#[derive(Clone, Copy, PartialEq, Debug)] -#[must_use] -pub struct BytecodeOffset { - pub offset: usize, -} - -impl BytecodeOffset { - fn new(offset: usize) -> Self { - Self { offset } - } - - pub fn end(self, emit: &InstructionWriter) -> usize { - // find the offset after the end of bytecode associated with this offset. - let target_opcode = Opcode::try_from(emit.bytecode[self.offset]).unwrap(); - self.offset + target_opcode.instruction_length() - } -} - -impl From for usize { - fn from(offset: BytecodeOffset) -> usize { - offset.offset - } -} - -pub struct BytecodeOffsetDiff { - pub diff: i32, -} - -impl BytecodeOffsetDiff { - pub fn new(offset1: BytecodeOffset, offset2: BytecodeOffset) -> Self { - let diff = (offset1.offset as f64 - offset2.offset as f64) as i32; - Self { diff } - } -} - /// Low-level bytecode emitter. pub struct InstructionWriter { bytecode: Vec, @@ -236,7 +201,7 @@ impl InstructionWriter { scope_notes: ScopeNoteList::new(), regexps: RegExpList::new(), last_jump_target_offset: None, - main_offset: BytecodeOffset::new(0), + main_offset: BytecodeOffset::from(0usize), max_fixed_slots: FrameSlot::new(0), stack_depth: 0, maximum_stack_depth: 0, @@ -325,6 +290,10 @@ impl InstructionWriter { self.write_i32(offset); } + fn write_bytecode_offset_diff(&mut self, offset: BytecodeOffsetDiff) { + self.write_i32(i32::from(offset)); + } + fn write_f64(&mut self, val: f64) { self.bytecode .extend_from_slice(&val.to_bits().to_le_bytes()); @@ -401,6 +370,12 @@ impl InstructionWriter { self.last_jump_target_offset = Some(target); } + fn get_end_of_bytecode(&mut self, offset: BytecodeOffset) -> usize { + // find the offset after the end of bytecode associated with this offset. + let target_opcode = Opcode::try_from(self.bytecode[offset.offset]).unwrap(); + offset.offset + target_opcode.instruction_length() + } + pub fn get_atom_index(&mut self, value: SourceAtomSetIndex) -> ScriptAtomSetIndex { self.atoms.insert(value) } @@ -410,7 +385,7 @@ impl InstructionWriter { let last_jump = self.last_jump_target_offset; match last_jump { Some(offset) => { - if offset.end(self) != target.offset { + if self.get_end_of_bytecode(offset) != target.offset { self.jump_target(); self.set_last_jump_target_offset(target); } else { @@ -429,14 +404,14 @@ impl InstructionWriter { } pub fn patch_jump_to_target(&mut self, target: BytecodeOffset, jump: BytecodeOffset) { - let diff = BytecodeOffsetDiff::new(target, jump).diff as i32; + let diff = target.diff_from(jump).into(); let index = jump.offset + 1; // FIXME: Use native endian instead of little endian LittleEndian::write_i32(&mut self.bytecode[index..index + 4], diff); } pub fn bytecode_offset(&mut self) -> BytecodeOffset { - BytecodeOffset::new(self.bytecode.len()) + BytecodeOffset::from(self.bytecode.len()) } pub fn stack_depth(&self) -> usize { @@ -1068,42 +1043,42 @@ impl InstructionWriter { pub fn goto_(&mut self, offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Goto); - self.write_i32(offset.diff); + self.write_bytecode_offset_diff(offset); } pub fn if_eq(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::IfEq); - self.write_i32(forward_offset.diff); + self.write_bytecode_offset_diff(forward_offset); } pub fn if_ne(&mut self, offset: BytecodeOffsetDiff) { self.emit_op(Opcode::IfNe); - self.write_i32(offset.diff); + self.write_bytecode_offset_diff(offset); } pub fn and_(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::And); - self.write_i32(forward_offset.diff); + self.write_bytecode_offset_diff(forward_offset); } pub fn or_(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Or); - self.write_i32(forward_offset.diff); + self.write_bytecode_offset_diff(forward_offset); } pub fn coalesce(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Coalesce); - self.write_i32(forward_offset.diff); + self.write_bytecode_offset_diff(forward_offset); } - pub fn case_(&mut self, forward_offset: i32) { + pub fn case_(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Case); - self.write_i32(forward_offset); + self.write_bytecode_offset_diff(forward_offset); } - pub fn default_(&mut self, forward_offset: i32) { + pub fn default_(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Default); - self.write_i32(forward_offset); + self.write_bytecode_offset_diff(forward_offset); } pub fn return_(&mut self) { @@ -1158,9 +1133,9 @@ impl InstructionWriter { self.write_u24(resume_index); } - pub fn gosub(&mut self, forward_offset: i32) { + pub fn gosub(&mut self, forward_offset: BytecodeOffsetDiff) { self.emit_op(Opcode::Gosub); - self.write_i32(forward_offset); + self.write_bytecode_offset_diff(forward_offset); } pub fn finally(&mut self) { diff --git a/crates/emitter/src/lib.rs b/crates/emitter/src/lib.rs index fe13eac8e..7eb5783ce 100644 --- a/crates/emitter/src/lib.rs +++ b/crates/emitter/src/lib.rs @@ -1,6 +1,7 @@ mod array_emitter; mod ast_emitter; mod block_emitter; +mod bytecode_offset; mod compilation_info; mod control_structures; mod dis; diff --git a/crates/emitter/src/scope_notes.rs b/crates/emitter/src/scope_notes.rs index 679870561..6d736fddd 100644 --- a/crates/emitter/src/scope_notes.rs +++ b/crates/emitter/src/scope_notes.rs @@ -1,4 +1,4 @@ -use crate::emitter::BytecodeOffset; +use crate::bytecode_offset::BytecodeOffset; use crate::gcthings::GCThingIndex; /// Maps to js::ScopeNote in m-c/js/src/vm//JSScript.h.