11use enum_map:: Enum ;
2+ use std:: collections:: HashMap ;
3+ use ir:: Bytecode ;
24
3- pub trait Pass : Send + Sync {
4- fn optimize ( & mut self , stmts : & mut Vec < u8 > ) -> anyhow:: Result < ( ) > ;
5+ use crate :: codegen:: ir:: module:: ZetaModule ;
56
7+ use std:: sync:: { Arc , Mutex } ;
8+
9+ pub trait Pass : Send + Sync + ' static {
10+ fn optimize ( & self , bytecode : & mut Vec < u8 > , module : & ZetaModule ) -> anyhow:: Result < ( ) > ;
611 fn priority ( & self ) -> OptimizationPassPriority ;
12+
13+ /// Create a new boxed instance of the pass (for thread-safe cloning)
14+ #[ inline]
15+ fn boxed ( self ) -> Box < dyn Pass >
16+ where
17+ Self : Sized + ' static
18+ {
19+ Box :: new ( self )
20+ }
721}
822
923#[ derive( Copy , Clone , Hash , Eq , PartialEq , Debug , Enum ) ]
@@ -48,39 +62,278 @@ impl Default for OptimizationPassPriority {
4862pub struct ConstantFoldingPass ;
4963
5064impl Pass for ConstantFoldingPass {
51- fn optimize ( & mut self , bytecode : & mut Vec < u8 > ) -> anyhow:: Result < ( ) > {
52- for stmt in bytecode. iter_mut ( ) {
53- // TODO: check the bytecode, and if it's a math operation with two constants then let's replace it with a constant
54-
55- }
56- todo ! ( )
65+ fn optimize ( & self , bytecode : & mut Vec < u8 > , _module : & ZetaModule ) -> anyhow:: Result < ( ) > {
66+ let mut i = 0 ;
67+ while i < bytecode. len ( ) {
68+ // Check if we have a binary operation with two constants before it
69+ if i >= 2 && Self :: is_binary_op ( bytecode[ i] ) {
70+ let op = bytecode[ i] ;
71+ // Check if the previous two instructions are constants
72+ if i >= 2 && Self :: is_constant_load ( & bytecode[ i-2 ..i] ) {
73+ // Get the two constant values
74+ let ( val1, val1_size) = Self :: get_constant_value ( & bytecode[ ..i-1 ] ) ;
75+ let ( val2, val2_size) = Self :: get_constant_value ( & bytecode[ i-1 ..i+1 ] ) ;
76+
77+ if let Some ( result) = Self :: evaluate_binary_op ( op, val1, val2) {
78+ let result_bytes = Self :: get_constant_bytes ( result) ;
79+ let replace_start = i - val1_size - val2_size;
80+ let replace_end = i + 1 ;
81+
82+ let len = result_bytes. len ( ) ;
83+ bytecode. splice ( replace_start..replace_end, result_bytes) ;
84+ i = replace_start + len;
85+ continue ;
86+ }
87+ }
88+ }
89+ i += 1 ;
90+ }
91+ Ok ( ( ) )
5792 }
5893
94+
5995 fn priority ( & self ) -> OptimizationPassPriority {
60- OptimizationPassPriority :: Min
96+ OptimizationPassPriority :: Medium
97+ }
98+ }
99+
100+ impl ConstantFoldingPass {
101+ fn is_binary_op ( op : u8 ) -> bool {
102+ matches ! ( Bytecode :: from( op) ,
103+ Bytecode :: Add | Bytecode :: Sub |
104+ Bytecode :: Mul | Bytecode :: Div |
105+ Bytecode :: Mod | Bytecode :: BitOr |
106+ Bytecode :: BitAnd | Bytecode :: BitXor |
107+ Bytecode :: Shl | Bytecode :: Shr
108+ )
109+ }
110+
111+ fn is_constant_load ( ops : & [ u8 ] ) -> bool {
112+ matches ! ( Bytecode :: from( ops[ 0 ] ) ,
113+ Bytecode :: PushI32 | Bytecode :: PushI64 |
114+ Bytecode :: PushU8 | Bytecode :: PushU16 |
115+ Bytecode :: PushU32 | Bytecode :: PushU64
116+ )
117+ }
118+
119+ fn get_constant_value ( ops : & [ u8 ] ) -> ( i64 , usize ) {
120+ match Bytecode :: from ( ops[ 0 ] ) {
121+ Bytecode :: PushI32 => ( i32:: from_le_bytes ( [ ops[ 1 ] , ops[ 2 ] , ops[ 3 ] , ops[ 4 ] ] ) as i64 , 5 ) ,
122+ Bytecode :: PushI64 =>
123+ ( i64:: from_le_bytes ( [ ops[ 1 ] , ops[ 2 ] , ops[ 3 ] , ops[ 4 ] , ops[ 5 ] , ops[ 6 ] , ops[ 7 ] , ops[ 8 ] ] ) , 9 ) ,
124+ Bytecode :: PushU8 => ( ops[ 1 ] as i64 , 2 ) ,
125+ Bytecode :: PushU16 => ( u16:: from_le_bytes ( [ ops[ 1 ] , ops[ 2 ] ] ) as i64 , 3 ) ,
126+ Bytecode :: PushU32 => ( u32:: from_le_bytes ( [ ops[ 1 ] , ops[ 2 ] , ops[ 3 ] , ops[ 4 ] ] ) as i64 , 5 ) ,
127+ Bytecode :: PushU64 =>
128+ ( u64:: from_le_bytes ( [ ops[ 1 ] , ops[ 2 ] , ops[ 3 ] , ops[ 4 ] , ops[ 5 ] , ops[ 6 ] , ops[ 7 ] , ops[ 8 ] ] ) as i64 , 9 ) ,
129+ _ => ( 0 , 0 ) ,
130+ }
131+ }
132+
133+ fn evaluate_binary_op ( op : u8 , left : i64 , right : i64 ) -> Option < i64 > {
134+ match Bytecode :: from ( op) {
135+ Bytecode :: Add => left. checked_add ( right) ,
136+ Bytecode :: Sub => left. checked_sub ( right) ,
137+ Bytecode :: Mul => left. checked_mul ( right) ,
138+ Bytecode :: Div => left. checked_div ( right) ,
139+ Bytecode :: Mod => left. checked_rem ( right) ,
140+ Bytecode :: BitOr => Some ( left | right) ,
141+ Bytecode :: BitAnd => Some ( left & right) ,
142+ Bytecode :: BitXor => Some ( left ^ right) ,
143+ Bytecode :: Shl => left. checked_shl ( right as u32 ) ,
144+ Bytecode :: Shr => left. checked_shr ( right as u32 ) ,
145+ _ => None ,
146+ }
147+ }
148+
149+ fn get_constant_bytes ( value : i64 ) -> Vec < u8 > {
150+ if value >= i8:: MIN as i64 && value <= i8:: MAX as i64 {
151+ vec ! [ Bytecode :: PushI8 as u8 , value as u8 ]
152+ } else if value >= i16:: MIN as i64 && value <= i16:: MAX as i64 {
153+ let bytes = ( value as i16 ) . to_le_bytes ( ) ;
154+ vec ! [ Bytecode :: PushI16 as u8 , bytes[ 0 ] , bytes[ 1 ] ]
155+ } else if value >= i32:: MIN as i64 && value <= i32:: MAX as i64 {
156+ let bytes = ( value as i32 ) . to_le_bytes ( ) ;
157+ vec ! [ Bytecode :: PushI32 as u8 , bytes[ 0 ] , bytes[ 1 ] , bytes[ 2 ] , bytes[ 3 ] ]
158+ } else {
159+ let bytes = value. to_le_bytes ( ) ;
160+ let mut result = vec ! [ Bytecode :: PushI64 as u8 ] ;
161+ result. extend_from_slice ( & bytes) ;
162+ result
163+ }
61164 }
62165}
63166
64167pub struct DeadCodeEliminationPass ;
65168
66169impl Pass for DeadCodeEliminationPass {
67- fn optimize ( & mut self , bytecode : & mut Vec < u8 > ) -> anyhow:: Result < ( ) > {
68- todo ! ( )
170+ fn optimize ( & self , bytecode : & mut Vec < u8 > , _module : & ZetaModule ) -> anyhow:: Result < ( ) > {
171+ let mut i = 0 ;
172+ while i < bytecode. len ( ) {
173+ match Bytecode :: from ( bytecode[ i] ) {
174+ // Remove unreachable code after return/break/continue/throw
175+ Bytecode :: Return | Bytecode :: Halt => {
176+ // Remove all instructions until the next label or end of function
177+ let mut j = i + 1 ;
178+ while j < bytecode. len ( ) {
179+ if Self :: is_control_flow ( & bytecode[ j..] ) {
180+ break ;
181+ }
182+ j += 1 ;
183+ }
184+ if j > i + 1 {
185+ bytecode. drain ( i+1 ..j) ;
186+ }
187+ }
188+ // Remove dead stores (store followed by another store to same location)
189+ Bytecode :: StoreLocal | Bytecode :: StoreGlobal | Bytecode :: StoreVar => {
190+ if let Some ( loc) = Self :: get_store_location ( & bytecode[ i..] ) {
191+ let mut j = i + loc. len ( ) ;
192+ while j < bytecode. len ( ) {
193+ if Self :: is_control_flow ( & bytecode[ j..] ) {
194+ break ;
195+ }
196+
197+ // Check if this is a store to the same location
198+ if let Some ( other_loc) = Self :: get_store_location ( & bytecode[ j..] ) {
199+ if other_loc == loc {
200+ // Found a store to the same location, remove the first one
201+ bytecode. drain ( i..i+loc. len ( ) ) ;
202+ i -= 1 ; // Adjust index since we removed items
203+ break ;
204+ }
205+ j += other_loc. len ( ) ;
206+ } else {
207+ j += 1 ;
208+ }
209+ }
210+ }
211+ }
212+ _ => { }
213+ }
214+ i += 1 ;
215+ }
216+ Ok ( ( ) )
69217 }
70218
71219 fn priority ( & self ) -> OptimizationPassPriority {
72- OptimizationPassPriority :: Min
220+ OptimizationPassPriority :: Medium
221+ }
222+ }
223+
224+ impl DeadCodeEliminationPass {
225+ fn is_control_flow ( bytecode : & [ u8 ] ) -> bool {
226+ matches ! (
227+ Bytecode :: from( bytecode[ 0 ] ) ,
228+ Bytecode :: Jump
229+ | Bytecode :: JumpIfTrue
230+ | Bytecode :: JumpIfFalse
231+ | Bytecode :: Return
232+ | Bytecode :: Halt
233+ | Bytecode :: Call
234+ | Bytecode :: TailCall
235+ | Bytecode :: CallNative
236+ )
237+ }
238+
239+ fn get_store_location ( bytecode : & [ u8 ] ) -> Option < Vec < u8 > > {
240+ match Bytecode :: from ( bytecode[ 0 ] ) {
241+ Bytecode :: StoreLocal | Bytecode :: LoadLocal => {
242+ if bytecode. len ( ) > 1 {
243+ Some ( bytecode[ 0 ..2 ] . to_vec ( ) )
244+ } else {
245+ None
246+ }
247+ }
248+ Bytecode :: StoreGlobal | Bytecode :: LoadGlobal => {
249+ if bytecode. len ( ) > 1 {
250+ Some ( bytecode[ 0 ..2 ] . to_vec ( ) )
251+ } else {
252+ None
253+ }
254+ }
255+ Bytecode :: StoreVar | Bytecode :: LoadVar => {
256+ if bytecode. len ( ) > 1 {
257+ // For StoreVar, the name length is the next byte
258+ let name_len = bytecode[ 1 ] as usize ;
259+ if bytecode. len ( ) > 1 + name_len {
260+ Some ( bytecode[ 0 ..2 + name_len] . to_vec ( ) )
261+ } else {
262+ None
263+ }
264+ } else {
265+ None
266+ }
267+ }
268+ _ => None ,
269+ }
73270 }
74271}
75272
76- pub struct InliningPass ;
273+
274+ pub struct InliningPass {
275+ // Maximum size of function to inline (in bytes)
276+ max_inline_size : usize ,
277+ }
278+
279+ impl Default for InliningPass {
280+ fn default ( ) -> Self {
281+ Self {
282+ max_inline_size : 120 , // Reasonable default for small functions
283+ }
284+ }
285+ }
77286
78287impl Pass for InliningPass {
79- fn optimize ( & mut self , bytecode : & mut Vec < u8 > ) -> anyhow:: Result < ( ) > {
80- todo ! ( )
288+ fn optimize ( & self , bytecode : & mut Vec < u8 > , module : & ZetaModule ) -> anyhow:: Result < ( ) > {
289+ let mut i = 0 ;
290+ while i < bytecode. len ( ) {
291+ if i + 1 < bytecode. len ( ) && Bytecode :: from ( bytecode[ i] ) == Bytecode :: Call {
292+ // Get the function name
293+ if let Some ( ( name, name_len) ) = self . get_function_name ( & bytecode[ i+1 ..] ) {
294+ // Look up the function in the module
295+ if let Some ( ( _, func) ) = module. functions . iter ( ) . find ( |( _, f) | f. name == name) {
296+ // Check if the function is small enough to inline
297+ if func. code . len ( ) <= self . max_inline_size {
298+ // Replace the call with the function body
299+ let call_size = 1 + 1 + name_len; // CALL op + name length + name
300+ let mut new_code = func. code . clone ( ) ;
301+
302+ // Replace return with jump to the end of inlined code
303+ if let Some ( return_pos) = new_code. iter ( ) . position ( |& b| Bytecode :: from ( b) == Bytecode :: Return ) {
304+ new_code. truncate ( return_pos) ;
305+ }
306+
307+ let len = new_code. len ( ) ;
308+ bytecode. splice ( i..i+call_size, new_code) ;
309+ i += len;
310+ continue ;
311+ }
312+ }
313+ }
314+ }
315+ i += 1 ;
316+ }
317+ Ok ( ( ) )
81318 }
82319
83320 fn priority ( & self ) -> OptimizationPassPriority {
84321 OptimizationPassPriority :: Max
85322 }
323+ }
324+
325+ impl InliningPass {
326+ fn get_function_name ( & self , bytecode : & [ u8 ] ) -> Option < ( String , usize ) > {
327+ if bytecode. is_empty ( ) {
328+ return None ;
329+ }
330+
331+ let name_len = bytecode[ 0 ] as usize ;
332+ if bytecode. len ( ) > name_len {
333+ let name = String :: from_utf8_lossy ( & bytecode[ 1 ..=name_len] ) . into_owned ( ) ;
334+ Some ( ( name, name_len + 1 ) ) // +1 for the length byte
335+ } else {
336+ None
337+ }
338+ }
86339}
0 commit comments