-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuilder.rs
More file actions
131 lines (115 loc) · 4.38 KB
/
Copy pathbuilder.rs
File metadata and controls
131 lines (115 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::chunk::Chunk;
use crate::constant_pool::ConstantPool;
use crate::debug::DebugSymbols;
use crate::function::BytecodeFunction;
use crate::instruction::BytecodeInstruction;
use crate::opcode::Opcode;
use crate::operand::Operand;
use crate::source_map::SourceMap;
use std::collections::HashMap;
use techscript_common::Span;
use techscript_ir::types::InstructionId;
/// Reference placeholder for relative branch offsets.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Label(pub u32);
/// Assembler emitting instructions with lazy label references and auto stack estimation.
pub struct BytecodeBuilder {
pub name: String,
pub param_count: u32,
pub local_count: u32,
pub instructions: Vec<BytecodeInstruction>,
pub constants: ConstantPool,
pub source_map: SourceMap,
pub debug_symbols: DebugSymbols,
label_counter: u32,
labels: HashMap<u32, Option<u32>>,
label_references: Vec<(usize, u32)>, // Instruction index -> Label ID
}
impl BytecodeBuilder {
/// Creates a new assembler for a function.
pub fn new(name: String, param_count: u32) -> Self {
Self {
name,
param_count,
local_count: 0,
instructions: Vec::new(),
constants: ConstantPool::new(),
source_map: SourceMap::new(),
debug_symbols: DebugSymbols::new(),
label_counter: 0,
labels: HashMap::new(),
label_references: Vec::new(),
}
}
/// Allocates a new local variable slot.
pub fn allocate_local(&mut self, name: String) -> u32 {
let idx = self.local_count;
self.local_count += 1;
self.debug_symbols.local_names.insert(idx, name);
idx
}
/// Creates a new unresolved label.
pub fn make_label(&mut self) -> Label {
let id = self.label_counter;
self.label_counter += 1;
self.labels.insert(id, None);
Label(id)
}
/// Binds the label to the current instruction offset point.
pub fn mark_label(&mut self, label: Label) {
let offset = self.instructions.len() as u32;
self.labels.insert(label.0, Some(offset));
}
/// Emits a single stack instruction.
pub fn emit(&mut self, op: Opcode, operands: Vec<Operand>, span: Span, inst_id: InstructionId) {
let offset = self.instructions.len() as u32;
self.source_map.add(offset, span);
let inst = BytecodeInstruction::new(inst_id, op, operands, span);
self.instructions.push(inst);
}
/// Emits a branch instruction targeting a lazy label.
pub fn emit_jump(&mut self, op: Opcode, label: Label, span: Span, inst_id: InstructionId) {
let offset = self.instructions.len();
self.label_references.push((offset, label.0));
// Placeholder offset operand
self.emit(op, vec![Operand::JumpOffset(9999)], span, inst_id);
}
/// Finalizes compilation, patching label offsets and calculating stack limits.
pub fn finish(mut self) -> BytecodeFunction {
// Patch jump offsets
for &(inst_idx, label_id) in &self.label_references {
if let Some(Some(target_offset)) = self.labels.get(&label_id) {
let jump_offset = (*target_offset as i32) - (inst_idx as i32);
if let Some(ref mut inst) = self.instructions.get_mut(inst_idx) {
inst.operands = vec![Operand::JumpOffset(jump_offset)];
}
}
}
// Calculate max stack size via static traversal
let mut max_stack_size = 0;
let mut curr_stack = 0i32;
for inst in &self.instructions {
let effect = inst.op.stack_effect();
curr_stack += effect;
if curr_stack < 0 {
// Stack underflows are normalized
curr_stack = 0;
}
if curr_stack > max_stack_size {
max_stack_size = curr_stack;
}
}
let mut chunk = Chunk::new();
chunk.instructions = std::mem::take(&mut self.instructions);
chunk.constants = std::mem::take(&mut self.constants);
BytecodeFunction {
name: self.name,
param_count: self.param_count,
local_count: self.local_count,
max_stack_size: max_stack_size as u32,
chunk,
source_map: self.source_map,
debug_symbols: self.debug_symbols,
}
}
}