44import logging
55from typing import Dict
66
7- from pythonbpf .type_deducer import ctypes_to_ir , is_ctypes , IntTy
7+ from pythonbpf .type_deducer import ctypes_to_ir , is_ctypes , IntTy , signedness
88from .call_registry import CallHandlerRegistry
99from .ir_ops import deref_to_depth , access_struct_field
10- from .operators import apply_binop , UNARY_OPS , BOOL_OPS
10+ from .operators import apply_binop , usual_arithmetic_conversions , UNARY_OPS , BOOL_OPS
1111from .type_normalization import (
1212 convert ,
13+ to_promoted ,
14+ canonicalise ,
1315 convert_to_bool ,
1416 handle_comparator ,
1517 get_base_type_and_depth ,
@@ -177,71 +179,89 @@ def _handle_deref_call(expr: ast.Call, local_sym_tab: Dict, builder: ir.IRBuilde
177179# ============================================================================
178180
179181
180- def get_operand_value (func , compilation_context , operand , builder , local_sym_tab ):
181- """Extract the value from an operand, handling variables and constants."""
182- logger .info (f"Getting operand value for: { ast .dump (operand )} " )
182+ def _descriptor (val , ty ):
183+ """IntTy descriptor for an evaluated integer value: width from the physical
184+ value unless the descriptor is itself an integer type, sign from the
185+ descriptor (an IntTy, a vmlinux Field, or plain -> signed)."""
186+ width = ty .width if isinstance (ty , ir .IntType ) else val .type .width
187+ return IntTy (width , signedness (ty ))
188+
189+
190+ def get_typed_operand (func , compilation_context , operand , builder , local_sym_tab ):
191+ """Evaluate an operand to (value, IntTy). Pointers (map-lookup results) are
192+ dereferenced to the scalar they point at."""
193+ logger .info (f"Getting typed operand for: { ast .dump (operand )} " )
183194 if isinstance (operand , ast .Name ):
184195 if operand .id in local_sym_tab :
185- var = local_sym_tab [operand .id ].var
186- var_type = var .type
187- base_type , depth = get_base_type_and_depth (var_type )
188- logger .info (f"var is { var } , base_type is { base_type } , depth is { depth } " )
189- if depth == 1 :
190- val = builder .load (var )
191- return val
192- else :
193- val = deref_to_depth (func , builder , var , depth )
194- return val
196+ sym = local_sym_tab [operand .id ]
197+ var = sym .var
198+ base_type , depth = get_base_type_and_depth (var .type )
199+ val = (
200+ builder .load (var )
201+ if depth == 1
202+ else deref_to_depth (func , builder , var , depth )
203+ )
204+ return val , _descriptor (val , sym .ir_type if depth == 1 else base_type )
195205 elif operand .id in compilation_context .bpf_globals :
196- # A @bpfglobal: plain load off the global symbol.
197- return builder .load (compilation_context . bpf_globals [ operand . id ]. var )
206+ sym = compilation_context . bpf_globals [ operand . id ]
207+ return builder .load (sym . var ), _descriptor ( None , sym . ir_type )
198208 else :
199- # Check if it's a vmlinux enum/constant
200209 vmlinux_result = VmlinuxHandlerRegistry .handle_name (operand .id )
201210 if vmlinux_result is not None :
202211 val , _ = vmlinux_result
203- return val
212+ return val , IntTy ( 64 , True )
204213 elif isinstance (operand , ast .Constant ):
205- if isinstance (operand .value , int ):
206- cst = ir .Constant (ir .IntType (64 ), int (operand .value ))
207- return cst
214+ if isinstance (operand .value , (int , bool )):
215+ v = int (operand .value )
216+ lit_ty = IntTy (32 , True ) if - (1 << 31 ) <= v < (1 << 31 ) else IntTy (64 , True )
217+ return ir .Constant (ir .IntType (64 ), v ), lit_ty
208218 raise TypeError (f"Unsupported constant type: { type (operand .value )} " )
209219 elif isinstance (operand , ast .BinOp ):
210- res = _handle_binary_op_impl (
220+ return _handle_binary_op_impl (
211221 func , compilation_context , operand , builder , local_sym_tab
212222 )
213- return res
214223 else :
215224 res = eval_expr (func , compilation_context , builder , operand , local_sym_tab )
216225 if res is None :
217226 raise ValueError (f"Failed to evaluate call expression: { operand } " )
218- val , _ = res
227+ val , ty = res
219228 logger .info (f"Evaluated expr to { val } of type { val .type } " )
220229 base_type , depth = get_base_type_and_depth (val .type )
221230 if depth > 0 :
222231 val = deref_to_depth (func , builder , val , depth )
223- return val
232+ return val , _descriptor ( val , ty )
224233 raise TypeError (f"Unsupported operand type: { type (operand )} " )
225234
226235
236+ def get_operand_value (func , compilation_context , operand , builder , local_sym_tab ):
237+ """Extract the value from an operand, handling variables and constants."""
238+ return get_typed_operand (
239+ func , compilation_context , operand , builder , local_sym_tab
240+ )[0 ]
241+
242+
227243def _handle_binary_op_impl (func , compilation_context , rval , builder , local_sym_tab ):
244+ """A binary operation, typed per node the way C types it: the operation is
245+ performed in the type given by the usual arithmetic conversions of its two
246+ operands, each operand converted to that type first, and the result
247+ narrowed to it -- so u32 * u32 wraps at 32 bits even though the arithmetic
248+ itself runs in an i64 register. Returns (value, IntTy)."""
228249 op = rval .op
229- left = get_operand_value (
250+ left , left_ty = get_typed_operand (
230251 func , compilation_context , rval .left , builder , local_sym_tab
231252 )
232- right = get_operand_value (
253+ right , right_ty = get_typed_operand (
233254 func , compilation_context , rval .right , builder , local_sym_tab
234255 )
235- logger .info (f"left is { left } , right is { right } , op is { op } " )
236-
237- # NOTE: Before doing the operation, if the operands are integers
238- # we always extend them to i64. The assignment to LHS will take
239- # care of truncation if needed.
240- left = convert (builder , left , left .type , ir .IntType (64 ))
241- right = convert (builder , right , right .type , ir .IntType (64 ))
242-
243- # Map AST operation nodes to LLVM IR builder methods
244- return apply_binop (builder , op , left , right )
256+ result_ty = usual_arithmetic_conversions (left_ty , right_ty )
257+ logger .info (
258+ f"binop { type (op ).__name__ } : { left_ty .describe ()} x { right_ty .describe ()} "
259+ f"-> { result_ty .describe ()} "
260+ )
261+ left = to_promoted (builder , left , left_ty , result_ty )
262+ right = to_promoted (builder , right , right_ty , result_ty )
263+ result = apply_binop (builder , op , left , right )
264+ return canonicalise (builder , result , result_ty ), result_ty
245265
246266
247267def _handle_binary_op (
@@ -252,15 +272,14 @@ def _handle_binary_op(
252272 var_name ,
253273 local_sym_tab ,
254274):
255- result = _handle_binary_op_impl (
275+ result , result_ty = _handle_binary_op_impl (
256276 func , compilation_context , rval , builder , local_sym_tab
257277 )
258278 if var_name and var_name in local_sym_tab :
259- logger .info (
260- f"Storing result { result } into variable { local_sym_tab [var_name ].var } "
261- )
262- builder .store (result , local_sym_tab [var_name ].var )
263- return result , result .type
279+ slot = local_sym_tab [var_name ]
280+ logger .info (f"Storing result { result } into variable { slot .var } " )
281+ builder .store (convert (builder , result , result_ty , slot .ir_type ), slot .var )
282+ return result , result_ty
264283
265284
266285# ============================================================================
@@ -368,7 +387,7 @@ def _handle_unary_op(
368387 logger .error ("Only 'not' and '-' unary operators are supported" )
369388 return None
370389
371- operand = get_operand_value (
390+ operand , operand_ty = get_typed_operand (
372391 func , compilation_context , expr .operand , builder , local_sym_tab
373392 )
374393 if operand is None :
@@ -380,10 +399,12 @@ def _handle_unary_op(
380399 result = builder .xor (convert_to_bool (builder , operand ), true_const )
381400 return result , ir .IntType (1 )
382401 elif isinstance (expr .op , ast .USub ):
383- # Multiply by -1
384- neg_one = ir .Constant (ir .IntType (64 ), - 1 )
385- result = builder .mul (operand , neg_one )
386- return result , ir .IntType (64 )
402+ # Negation happens in the operand's promoted type; for an unsigned
403+ # operand that is C's 2^N - x, which the narrowing produces.
404+ result_ty = usual_arithmetic_conversions (operand_ty , operand_ty )
405+ operand = to_promoted (builder , operand , operand_ty , result_ty )
406+ result = builder .mul (operand , ir .Constant (ir .IntType (64 ), - 1 ))
407+ return canonicalise (builder , result , result_ty ), result_ty
387408 return None
388409
389410
0 commit comments