1- // Copyright 2022 Oxide Computer Company
1+ // Copyright 2026 Oxide Computer Company
22
33use p4:: ast:: { BinOp , DeclarationInfo , Expression , ExpressionKind , Lvalue } ;
44use p4:: hlir:: Hlir ;
@@ -101,6 +101,11 @@ impl<'a> ExpressionGenerator<'a> {
101101 ts. extend ( op_tks) ;
102102 ts. extend ( rhs_tks_) ;
103103 }
104+ BinOp :: BitOr | BinOp :: BitAnd | BinOp :: Xor | BinOp :: Mask => {
105+ ts. extend ( quote ! {
106+ #lhs_tks. clone( ) #op_tks #rhs_tks. clone( )
107+ } ) ;
108+ }
104109 _ => {
105110 ts. extend ( lhs_tks) ;
106111 ts. extend ( op_tks) ;
@@ -111,22 +116,42 @@ impl<'a> ExpressionGenerator<'a> {
111116 }
112117 ExpressionKind :: Index ( lval, xpr) => {
113118 let mut ts = self . generate_lvalue ( lval) ;
114- ts. extend ( self . generate_expression ( xpr. as_ref ( ) ) ) ;
119+ // For slices, look up the parent field's bit width
120+ // so generate_slice can adjust for header.rs byte
121+ // reversal.
122+ if let ExpressionKind :: Slice ( begin, end) = & xpr. kind {
123+ let ni =
124+ self . hlir . lvalue_decls . get ( lval) . unwrap_or_else ( || {
125+ panic ! ( "unresolved lvalue {:#?} in slice" , lval)
126+ } ) ;
127+
128+ let field_width = match & ni. ty {
129+ p4:: ast:: Type :: Bit ( w)
130+ | p4:: ast:: Type :: Varbit ( w)
131+ | p4:: ast:: Type :: Int ( w) => * w,
132+ ty => panic ! (
133+ "slice on non-bit type {:?} reached codegen" ,
134+ ty,
135+ ) ,
136+ } ;
137+ let ( hi, lo) = Self :: slice_bounds ( begin, end) ;
138+ if Self :: slice_is_contiguous ( hi, lo, field_width) {
139+ ts. extend ( self . generate_slice ( begin, end, field_width) ) ;
140+ } else {
141+ // Non-contiguous after byte reversal;
142+ // replace the lvalue suffix with arithmetic.
143+ return Self :: generate_slice_read_arith ( & ts, hi, lo) ;
144+ }
145+ } else {
146+ ts. extend ( self . generate_expression ( xpr. as_ref ( ) ) ) ;
147+ }
115148 ts
116149 }
117- ExpressionKind :: Slice ( begin, end) => {
118- let l = match & begin. kind {
119- ExpressionKind :: IntegerLit ( v) => * v as usize ,
120- _ => panic ! ( "slice ranges can only be integer literals" ) ,
121- } ;
122- let l = l + 1 ;
123- let r = match & end. kind {
124- ExpressionKind :: IntegerLit ( v) => * v as usize ,
125- _ => panic ! ( "slice ranges can only be integer literals" ) ,
126- } ;
127- quote ! {
128- [ #r..#l]
129- }
150+ ExpressionKind :: Slice ( _begin, _end) => {
151+ // The HLIR rejects bare slices outside an Index
152+ // expression, so this is unreachable for well-typed
153+ // programs.
154+ unreachable ! ( "bare Slice reached codegen" ) ;
130155 }
131156 ExpressionKind :: Call ( call) => {
132157 let lv: Vec < TokenStream > = call
@@ -158,6 +183,79 @@ impl<'a> ExpressionGenerator<'a> {
158183 }
159184 }
160185
186+ /// Extract compile-time hi and lo from slice bound expressions.
187+ pub ( crate ) fn slice_bounds (
188+ begin : & Expression ,
189+ end : & Expression ,
190+ ) -> ( P4Bit , P4Bit ) {
191+ let hi: P4Bit = match & begin. kind {
192+ ExpressionKind :: IntegerLit ( v) => * v as usize ,
193+ _ => panic ! ( "slice ranges can only be integer literals" ) ,
194+ } ;
195+ let lo: P4Bit = match & end. kind {
196+ ExpressionKind :: IntegerLit ( v) => * v as usize ,
197+ _ => panic ! ( "slice ranges can only be integer literals" ) ,
198+ } ;
199+ ( hi, lo)
200+ }
201+
202+ /// Whether `[hi:lo]` on a field of `field_width` bits can be
203+ /// expressed as a contiguous bitvec range after byte reversal.
204+ pub ( crate ) fn slice_is_contiguous (
205+ hi : P4Bit ,
206+ lo : P4Bit ,
207+ field_width : FieldWidth ,
208+ ) -> bool {
209+ if field_width <= 8 {
210+ return true ;
211+ }
212+ reversed_slice_range ( hi, lo, field_width) . is_some ( )
213+ }
214+
215+ pub ( crate ) fn generate_slice (
216+ & self ,
217+ begin : & Expression ,
218+ end : & Expression ,
219+ field_width : FieldWidth ,
220+ ) -> TokenStream {
221+ let ( hi, lo) = Self :: slice_bounds ( begin, end) ;
222+
223+ if field_width > 8 {
224+ let ( r, l) = reversed_slice_range ( hi, lo, field_width) . expect (
225+ "non-contiguous slice reads must be handled \
226+ by the caller via generate_slice_read_arith",
227+ ) ;
228+ quote ! { [ #r..#l] }
229+ } else {
230+ // Fields <= 8 bits are not byte-reversed by header.rs,
231+ // so the naive P4-to-bitvec mapping is correct.
232+ let l = hi + 1 ;
233+ let r = lo;
234+ quote ! { [ #r..#l] }
235+ }
236+ }
237+
238+ /// Emit an arithmetic slice read for non-contiguous slices.
239+ /// Loads the field as an integer, shifts and masks to extract
240+ /// the requested bits, then packs into a new bitvec.
241+ pub ( crate ) fn generate_slice_read_arith (
242+ lhs : & TokenStream ,
243+ hi : P4Bit ,
244+ lo : P4Bit ,
245+ ) -> TokenStream {
246+ let slice_width = hi - lo + 1 ;
247+ let mask_val = ( 1u128 << slice_width) - 1 ;
248+ quote ! {
249+ {
250+ let __v: u128 = #lhs. load_le( ) ;
251+ let __extracted = ( __v >> #lo) & #mask_val;
252+ let mut __out = bitvec![ u8 , Msb0 ; 0 ; #slice_width] ;
253+ __out. store_le( __extracted) ;
254+ __out
255+ }
256+ }
257+ }
258+
161259 pub ( crate ) fn generate_bit_literal (
162260 & self ,
163261 width : u16 ,
@@ -223,3 +321,160 @@ impl<'a> ExpressionGenerator<'a> {
223321 }
224322 }
225323}
324+
325+ /// P4 bit position (MSB-first index within a field).
326+ type P4Bit = usize ;
327+
328+ /// Width of a P4 header field in bits.
329+ type FieldWidth = usize ;
330+
331+ /// Half-open bitvec range `(start, end)` into the storage representation.
332+ type BitvecRange = ( usize , usize ) ;
333+
334+ /// Map a P4 slice `[hi:lo]` to a bitvec range in byte-reversed storage.
335+ ///
336+ /// header.rs reverses byte order for fields wider than 8 bits. Bit
337+ /// positions within each byte are preserved (Msb0). The mapping from
338+ /// P4 bit positions to storage indices:
339+ ///
340+ /// ```text
341+ /// wire_idx = W - 1 - b
342+ /// wire_byte = wire_idx / 8
343+ /// bit_in_byte = wire_idx % 8
344+ /// storage_byte = W/8 - 1 - wire_byte
345+ /// bitvec_idx = storage_byte * 8 + bit_in_byte
346+ /// ```
347+ ///
348+ /// # Returns
349+ ///
350+ /// `Some(range)` when the slice maps to a contiguous bitvec range
351+ /// (single-byte slices or byte-aligned multi-byte slices), `None`
352+ /// for non-byte-aligned multi-byte slices where byte reversal makes
353+ /// the bits non-contiguous.
354+ pub ( crate ) fn reversed_slice_range (
355+ hi : P4Bit ,
356+ lo : P4Bit ,
357+ field_width : FieldWidth ,
358+ ) -> Option < BitvecRange > {
359+ // Wire byte indices for the slice endpoints. P4 bit W-1 is in wire
360+ // byte 0 (MSB-first), so higher bit numbers map to lower byte indices.
361+ let wire_byte_hi = ( field_width - 1 - hi) / 8 ;
362+ let wire_byte_lo = ( field_width - 1 - lo) / 8 ;
363+
364+ if wire_byte_hi == wire_byte_lo {
365+ // Single-byte slice: map each endpoint individually.
366+ let map_bit = |bit_pos : usize | -> usize {
367+ let wire_idx = field_width - 1 - bit_pos;
368+ let wire_byte = wire_idx / 8 ;
369+ let bit_in_byte = wire_idx % 8 ;
370+ let storage_byte = field_width / 8 - 1 - wire_byte;
371+ storage_byte * 8 + bit_in_byte
372+ } ;
373+
374+ let mapped_hi = map_bit ( hi) ;
375+ let mapped_lo = map_bit ( lo) ;
376+ Some ( ( mapped_hi. min ( mapped_lo) , mapped_hi. max ( mapped_lo) + 1 ) )
377+ } else if ( hi + 1 ) . is_multiple_of ( 8 ) && lo. is_multiple_of ( 8 ) {
378+ // Multi-byte byte-aligned slice: reversed bytes form a
379+ // contiguous block.
380+ let storage_byte_start = field_width / 8 - 1 - wire_byte_lo;
381+ let storage_byte_end = field_width / 8 - 1 - wire_byte_hi;
382+ Some ( ( storage_byte_start * 8 , ( storage_byte_end + 1 ) * 8 ) )
383+ } else {
384+ // Non-byte-aligned multi-byte slice: byte reversal makes the
385+ // bits non-contiguous, so there is no single bitvec range.
386+ None
387+ }
388+ }
389+
390+ #[ cfg( test) ]
391+ mod test {
392+ use super :: * ;
393+
394+ // Verify the reversed slice range mapping against the byte reversal
395+ // in header.rs. For each case we check that the bitvec range lands
396+ // on the correct bits in the reversed storage layout.
397+
398+ // Sub-byte slices within a single wire byte.
399+
400+ #[ test]
401+ fn slice_32bit_top_nibble ( ) {
402+ // P4 [31:28] on 32-bit: top nibble of wire byte 0.
403+ // Storage: wire byte 0 -> storage byte 3.
404+ // High nibble of storage byte 3 = bitvec [24..28].
405+ assert_eq ! ( reversed_slice_range( 31 , 28 , 32 ) , Some ( ( 24 , 28 ) ) ) ;
406+ }
407+
408+ #[ test]
409+ fn slice_32bit_bottom_nibble ( ) {
410+ // P4 [3:0] on 32-bit: bottom nibble of wire byte 3.
411+ // Storage: wire byte 3 -> storage byte 0.
412+ // Low nibble (Msb0) of storage byte 0 = bitvec [4..8].
413+ assert_eq ! ( reversed_slice_range( 3 , 0 , 32 ) , Some ( ( 4 , 8 ) ) ) ;
414+ }
415+
416+ #[ test]
417+ fn slice_16bit_top_nibble ( ) {
418+ // P4 [15:12] on 16-bit: top nibble of wire byte 0.
419+ // Storage: wire byte 0 -> storage byte 1.
420+ // High nibble of storage byte 1 = bitvec [8..12].
421+ assert_eq ! ( reversed_slice_range( 15 , 12 , 16 ) , Some ( ( 8 , 12 ) ) ) ;
422+ }
423+
424+ // Full-byte slices (single byte).
425+
426+ #[ test]
427+ fn slice_128bit_top_byte ( ) {
428+ // P4 [127:120] on 128-bit: wire byte 0 -> storage byte 15.
429+ // bitvec [120..128].
430+ assert_eq ! ( reversed_slice_range( 127 , 120 , 128 ) , Some ( ( 120 , 128 ) ) ) ;
431+ }
432+
433+ #[ test]
434+ fn slice_16bit_low_byte ( ) {
435+ // P4 [7:0] on 16-bit: wire byte 1 -> storage byte 0.
436+ // bitvec [0..8].
437+ assert_eq ! ( reversed_slice_range( 7 , 0 , 16 ) , Some ( ( 0 , 8 ) ) ) ;
438+ }
439+
440+ #[ test]
441+ fn slice_32bit_middle_byte ( ) {
442+ // P4 [23:16] on 32-bit: wire byte 1 -> storage byte 2.
443+ // bitvec [16..24].
444+ assert_eq ! ( reversed_slice_range( 23 , 16 , 32 ) , Some ( ( 16 , 24 ) ) ) ;
445+ }
446+
447+ // Multi-byte byte-aligned slices.
448+
449+ #[ test]
450+ fn slice_128bit_top_two_bytes ( ) {
451+ // P4 [127:112] on 128-bit: wire bytes 0-1 -> storage bytes 14-15.
452+ // bitvec [112..128].
453+ assert_eq ! ( reversed_slice_range( 127 , 112 , 128 ) , Some ( ( 112 , 128 ) ) ) ;
454+ }
455+
456+ #[ test]
457+ fn slice_32bit_top_three_bytes ( ) {
458+ // P4 [31:8] on 32-bit: wire bytes 0-2 -> storage bytes 1-3.
459+ // bitvec [8..32].
460+ assert_eq ! ( reversed_slice_range( 31 , 8 , 32 ) , Some ( ( 8 , 32 ) ) ) ;
461+ }
462+
463+ #[ test]
464+ fn slice_32bit_bottom_two_bytes ( ) {
465+ // P4 [15:0] on 32-bit: wire bytes 2-3 -> storage bytes 0-1.
466+ // bitvec [0..16].
467+ assert_eq ! ( reversed_slice_range( 15 , 0 , 32 ) , Some ( ( 0 , 16 ) ) ) ;
468+ }
469+
470+ #[ test]
471+ fn slice_48bit_upper_24 ( ) {
472+ assert_eq ! ( reversed_slice_range( 47 , 24 , 48 ) , Some ( ( 24 , 48 ) ) ) ;
473+ }
474+
475+ #[ test]
476+ fn slice_non_contiguous_returns_none ( ) {
477+ assert_eq ! ( reversed_slice_range( 11 , 4 , 32 ) , None ) ;
478+ assert_eq ! ( reversed_slice_range( 22 , 0 , 32 ) , None ) ;
479+ }
480+ }
0 commit comments