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 ,
@@ -180,71 +182,89 @@ def _handle_deref_call(expr: ast.Call, local_sym_tab: Dict, builder: ir.IRBuilde
180182# ============================================================================
181183
182184
183- def get_operand_value (func , compilation_context , operand , builder , local_sym_tab ):
184- """Extract the value from an operand, handling variables and constants."""
185- logger .info (f"Getting operand value for: { ast .dump (operand )} " )
185+ def _descriptor (val , ty ):
186+ """IntTy descriptor for an evaluated integer value: width from the physical
187+ value unless the descriptor is itself an integer type, sign from the
188+ descriptor (an IntTy, a vmlinux Field, or plain -> signed)."""
189+ width = ty .width if isinstance (ty , ir .IntType ) else val .type .width
190+ return IntTy (width , signedness (ty ))
191+
192+
193+ def get_typed_operand (func , compilation_context , operand , builder , local_sym_tab ):
194+ """Evaluate an operand to (value, IntTy). Pointers (map-lookup results) are
195+ dereferenced to the scalar they point at."""
196+ logger .info (f"Getting typed operand for: { ast .dump (operand )} " )
186197 if isinstance (operand , ast .Name ):
187198 if operand .id in local_sym_tab :
188- var = local_sym_tab [operand .id ].var
189- var_type = var .type
190- base_type , depth = get_base_type_and_depth (var_type )
191- logger .info (f"var is { var } , base_type is { base_type } , depth is { depth } " )
192- if depth == 1 :
193- val = builder .load (var )
194- return val
195- else :
196- val = deref_to_depth (func , builder , var , depth )
197- return val
199+ sym = local_sym_tab [operand .id ]
200+ var = sym .var
201+ base_type , depth = get_base_type_and_depth (var .type )
202+ val = (
203+ builder .load (var )
204+ if depth == 1
205+ else deref_to_depth (func , builder , var , depth )
206+ )
207+ return val , _descriptor (val , sym .ir_type if depth == 1 else base_type )
198208 elif operand .id in compilation_context .bpf_globals :
199- # A @bpfglobal: plain load off the global symbol.
200- return builder .load (compilation_context . bpf_globals [ operand . id ]. var )
209+ sym = compilation_context . bpf_globals [ operand . id ]
210+ return builder .load (sym . var ), _descriptor ( None , sym . ir_type )
201211 else :
202- # Check if it's a vmlinux enum/constant
203212 vmlinux_result = VmlinuxHandlerRegistry .handle_name (operand .id )
204213 if vmlinux_result is not None :
205214 val , _ = vmlinux_result
206- return val
215+ return val , IntTy ( 64 , True )
207216 elif isinstance (operand , ast .Constant ):
208- if isinstance (operand .value , int ):
209- cst = ir .Constant (ir .IntType (64 ), int (operand .value ))
210- return cst
217+ if isinstance (operand .value , (int , bool )):
218+ v = int (operand .value )
219+ lit_ty = IntTy (32 , True ) if - (1 << 31 ) <= v < (1 << 31 ) else IntTy (64 , True )
220+ return ir .Constant (ir .IntType (64 ), v ), lit_ty
211221 raise TypeError (f"Unsupported constant type: { type (operand .value )} " )
212222 elif isinstance (operand , ast .BinOp ):
213- res = _handle_binary_op_impl (
223+ return _handle_binary_op_impl (
214224 func , compilation_context , operand , builder , local_sym_tab
215225 )
216- return res
217226 else :
218227 res = eval_expr (func , compilation_context , builder , operand , local_sym_tab )
219228 if res is None :
220229 raise ValueError (f"Failed to evaluate call expression: { operand } " )
221- val , _ = res
230+ val , ty = res
222231 logger .info (f"Evaluated expr to { val } of type { val .type } " )
223232 base_type , depth = get_base_type_and_depth (val .type )
224233 if depth > 0 :
225234 val = deref_to_depth (func , builder , val , depth )
226- return val
235+ return val , _descriptor ( val , ty )
227236 raise TypeError (f"Unsupported operand type: { type (operand )} " )
228237
229238
239+ def get_operand_value (func , compilation_context , operand , builder , local_sym_tab ):
240+ """Extract the value from an operand, handling variables and constants."""
241+ return get_typed_operand (
242+ func , compilation_context , operand , builder , local_sym_tab
243+ )[0 ]
244+
245+
230246def _handle_binary_op_impl (func , compilation_context , rval , builder , local_sym_tab ):
247+ """A binary operation, typed per node the way C types it: the operation is
248+ performed in the type given by the usual arithmetic conversions of its two
249+ operands, each operand converted to that type first, and the result
250+ narrowed to it -- so u32 * u32 wraps at 32 bits even though the arithmetic
251+ itself runs in an i64 register. Returns (value, IntTy)."""
231252 op = rval .op
232- left = get_operand_value (
253+ left , left_ty = get_typed_operand (
233254 func , compilation_context , rval .left , builder , local_sym_tab
234255 )
235- right = get_operand_value (
256+ right , right_ty = get_typed_operand (
236257 func , compilation_context , rval .right , builder , local_sym_tab
237258 )
238- logger .info (f"left is { left } , right is { right } , op is { op } " )
239-
240- # NOTE: Before doing the operation, if the operands are integers
241- # we always extend them to i64. The assignment to LHS will take
242- # care of truncation if needed.
243- left = convert (builder , left , left .type , ir .IntType (64 ))
244- right = convert (builder , right , right .type , ir .IntType (64 ))
245-
246- # Map AST operation nodes to LLVM IR builder methods
247- return apply_binop (builder , op , left , right )
259+ result_ty = usual_arithmetic_conversions (left_ty , right_ty )
260+ logger .info (
261+ f"binop { type (op ).__name__ } : { left_ty .describe ()} x { right_ty .describe ()} "
262+ f"-> { result_ty .describe ()} "
263+ )
264+ left = to_promoted (builder , left , left_ty , result_ty )
265+ right = to_promoted (builder , right , right_ty , result_ty )
266+ result = apply_binop (builder , op , left , right )
267+ return canonicalise (builder , result , result_ty ), result_ty
248268
249269
250270def _handle_binary_op (
@@ -255,15 +275,14 @@ def _handle_binary_op(
255275 var_name ,
256276 local_sym_tab ,
257277):
258- result = _handle_binary_op_impl (
278+ result , result_ty = _handle_binary_op_impl (
259279 func , compilation_context , rval , builder , local_sym_tab
260280 )
261281 if var_name and var_name in local_sym_tab :
262- logger .info (
263- f"Storing result { result } into variable { local_sym_tab [var_name ].var } "
264- )
265- builder .store (result , local_sym_tab [var_name ].var )
266- return result , result .type
282+ slot = local_sym_tab [var_name ]
283+ logger .info (f"Storing result { result } into variable { slot .var } " )
284+ builder .store (convert (builder , result , result_ty , slot .ir_type ), slot .var )
285+ return result , result_ty
267286
268287
269288# ============================================================================
@@ -371,7 +390,7 @@ def _handle_unary_op(
371390 logger .error ("Only 'not' and '-' unary operators are supported" )
372391 return None
373392
374- operand = get_operand_value (
393+ operand , operand_ty = get_typed_operand (
375394 func , compilation_context , expr .operand , builder , local_sym_tab
376395 )
377396 if operand is None :
@@ -383,10 +402,12 @@ def _handle_unary_op(
383402 result = builder .xor (convert_to_bool (builder , operand ), true_const )
384403 return result , ir .IntType (1 )
385404 elif isinstance (expr .op , ast .USub ):
386- # Multiply by -1
387- neg_one = ir .Constant (ir .IntType (64 ), - 1 )
388- result = builder .mul (operand , neg_one )
389- return result , ir .IntType (64 )
405+ # Negation happens in the operand's promoted type; for an unsigned
406+ # operand that is C's 2^N - x, which the narrowing produces.
407+ result_ty = usual_arithmetic_conversions (operand_ty , operand_ty )
408+ operand = to_promoted (builder , operand , operand_ty , result_ty )
409+ result = builder .mul (operand , ir .Constant (ir .IntType (64 ), - 1 ))
410+ return canonicalise (builder , result , result_ty ), result_ty
390411 return None
391412
392413
0 commit comments