@@ -113,45 +113,132 @@ fn layout_branch() {
113113}
114114
115115#[ test]
116- fn layout_large_instruction_cache_alignment ( ) {
117- // Large instruction (Match48 = 48 bytes = 6 steps) near cache line boundary
118- // Start at step 5 (offset 40), would straddle - should pad
119- let large_match = MatchIR :: at ( Label ( 1 ) )
120- . nav ( Nav :: Down )
121- . node_type ( NodeTypeIR :: Named ( NonZeroU16 :: new ( 10 ) ) )
122- . pre_effect ( EffectIR :: start_obj ( ) )
123- . pre_effect ( EffectIR :: start_obj ( ) )
124- . pre_effect ( EffectIR :: start_obj ( ) )
125- . post_effect ( EffectIR :: node ( ) )
126- . post_effect ( EffectIR :: end_obj ( ) )
127- . post_effect ( EffectIR :: end_obj ( ) )
128- . post_effect ( EffectIR :: end_obj ( ) )
129- . next_many ( vec ! [
130- Label ( 100 ) ,
131- Label ( 101 ) ,
132- Label ( 102 ) ,
133- Label ( 103 ) ,
134- Label ( 104 ) ,
135- Label ( 105 ) ,
136- Label ( 106 ) ,
137- Label ( 107 ) ,
138- ] ) ;
139-
140- // Verify it's large enough to trigger alignment
141- assert ! ( large_match. size( ) >= 48 ) ;
116+ fn layout_match16_cache_alignment ( ) {
117+ // Match16 (16 bytes, 2 steps) at offset 56 would straddle (56+16=72 > 64)
118+ // Place 7 Match8s (7 steps = 56 bytes) before Match16
119+ // Expected: Match16 gets padded to step 8 (offset 64)
120+ let mut instructions = Vec :: new ( ) ;
121+
122+ // 7 Match8 instructions in a chain: Label(0) -> Label(1) -> ... -> Label(6) -> Label(7)
123+ for i in 0 ..7 {
124+ instructions. push (
125+ MatchIR :: at ( Label ( i) )
126+ . nav ( Nav :: Down )
127+ . next ( Label ( i + 1 ) )
128+ . into ( ) ,
129+ ) ;
130+ }
131+
132+ // Match16 at Label(7): needs 2+ successors to become Match16
133+ instructions. push (
134+ MatchIR :: at ( Label ( 7 ) )
135+ . nav ( Nav :: Down )
136+ . next_many ( vec ! [ Label ( 100 ) , Label ( 101 ) ] )
137+ . into ( ) ,
138+ ) ;
142139
143- let instructions = vec ! [
144- // Small instruction first
145- MatchIR :: epsilon( Label ( 0 ) , Label ( 1 ) ) . into( ) ,
146- large_match. into( ) ,
147- ] ;
140+ let result = CacheAligned :: layout ( & instructions, & [ Label ( 0 ) ] ) ;
141+
142+ // Labels 0-6 should be at steps 0-6 (no padding needed)
143+ for i in 0 ..7 {
144+ assert_eq ! (
145+ result. label_to_step. get( & Label ( i) ) ,
146+ Some ( & ( i as u16 ) ) ,
147+ "Label({i}) should be at step {i}"
148+ ) ;
149+ }
150+
151+ // Label(7) would be at step 7 (offset 56) without padding
152+ // But Match16 at offset 56 straddles (56+16=72 > 64), so it must be padded
153+ // After padding: step 8 (offset 64)
154+ let step7 = * result. label_to_step . get ( & Label ( 7 ) ) . unwrap ( ) ;
155+ assert_eq ! ( step7, 8 , "Match16 should be padded to step 8 (offset 64)" ) ;
156+
157+ // Total steps: 8 (padding at step 7) + 2 (Match16) = 10
158+ assert_eq ! ( result. total_steps, 10 ) ;
159+ }
160+
161+ #[ test]
162+ fn layout_match8_no_padding_needed ( ) {
163+ // Match8 (8 bytes) never straddles: max offset 56, 56+8=64 <= 64
164+ // Place 7 Match8s, then another Match8 - should NOT need padding
165+ let mut instructions = Vec :: new ( ) ;
166+
167+ for i in 0 ..8 {
168+ if i < 7 {
169+ instructions. push (
170+ MatchIR :: at ( Label ( i) )
171+ . nav ( Nav :: Down )
172+ . next ( Label ( i + 1 ) )
173+ . into ( ) ,
174+ ) ;
175+ } else {
176+ instructions. push ( MatchIR :: terminal ( Label ( i) ) . nav ( Nav :: Down ) . into ( ) ) ;
177+ }
178+ }
148179
149180 let result = CacheAligned :: layout ( & instructions, & [ Label ( 0 ) ] ) ;
150181
151- // Label 0 at step 0 (offset 0)
152- assert_eq ! ( result. label_to_step. get( & Label ( 0 ) ) , Some ( & 0u16 ) ) ;
182+ // All 8 Match8s should be contiguous: steps 0-7
183+ for i in 0 ..8 {
184+ assert_eq ! (
185+ result. label_to_step. get( & Label ( i) ) ,
186+ Some ( & ( i as u16 ) ) ,
187+ "Label({i}) should be at step {i} (no padding)"
188+ ) ;
189+ }
190+
191+ // Total steps: 8 (no padding)
192+ assert_eq ! ( result. total_steps, 8 ) ;
193+ }
194+
195+ #[ test]
196+ fn layout_match32_cache_alignment ( ) {
197+ // Match32 (32 bytes, 4 steps) at offset 40 would straddle (40+32=72 > 64)
198+ // Place 5 Match8s (5 steps = 40 bytes) before Match32
199+ // Expected: Match32 gets padded to step 8 (offset 64)
200+ let mut instructions: Vec < crate :: bytecode:: InstructionIR > = Vec :: new ( ) ;
201+
202+ // 5 Match8 instructions: Label(0) -> ... -> Label(4) -> Label(5)
203+ for i in 0 ..5 {
204+ instructions. push (
205+ MatchIR :: at ( Label ( i) )
206+ . nav ( Nav :: Down )
207+ . next ( Label ( i + 1 ) )
208+ . into ( ) ,
209+ ) ;
210+ }
211+
212+ // Match32 at Label(5): needs enough payload to become Match32 (9-12 slots)
213+ // 3 pre + 3 post + 4 successors = 10 slots -> Match32
214+ instructions. push (
215+ MatchIR :: at ( Label ( 5 ) )
216+ . nav ( Nav :: Down )
217+ . pre_effect ( EffectIR :: start_obj ( ) )
218+ . pre_effect ( EffectIR :: start_obj ( ) )
219+ . pre_effect ( EffectIR :: start_obj ( ) )
220+ . post_effect ( EffectIR :: end_obj ( ) )
221+ . post_effect ( EffectIR :: end_obj ( ) )
222+ . post_effect ( EffectIR :: end_obj ( ) )
223+ . next_many ( vec ! [ Label ( 100 ) , Label ( 101 ) , Label ( 102 ) , Label ( 103 ) ] )
224+ . into ( ) ,
225+ ) ;
226+
227+ // Verify it's Match32 (32 bytes)
228+ assert_eq ! ( instructions. last( ) . unwrap( ) . size( ) , 32 ) ;
229+
230+ let result = CacheAligned :: layout ( & instructions, & [ Label ( 0 ) ] ) ;
231+
232+ // Labels 0-4 at steps 0-4
233+ for i in 0 ..5 {
234+ assert_eq ! ( result. label_to_step. get( & Label ( i) ) , Some ( & ( i as u16 ) ) ) ;
235+ }
236+
237+ // Label(5) would be at step 5 (offset 40) without padding
238+ // Match32 at offset 40 straddles (40+32=72 > 64), so padded to step 8
239+ let step5 = * result. label_to_step . get ( & Label ( 5 ) ) . unwrap ( ) ;
240+ assert_eq ! ( step5, 8 , "Match32 should be padded to step 8 (offset 64)" ) ;
153241
154- // Label 1 should be aligned - either at step 1 or padded to cache line
155- let step1 = * result. label_to_step . get ( & Label ( 1 ) ) . unwrap ( ) ;
156- assert ! ( step1 >= 1 ) ;
242+ // Total steps: 8 (3 padding steps at 5,6,7) + 4 (Match32) = 12
243+ assert_eq ! ( result. total_steps, 12 ) ;
157244}
0 commit comments