Skip to content

Commit e0ad1bf

Browse files
committed
Move bulk of allocation logic to allocation_pass
1 parent 69bee5f commit e0ad1bf

File tree

3 files changed

+196
-202
lines changed

3 files changed

+196
-202
lines changed

pythonbpf/allocation_pass.py

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import ast
2+
import logging
3+
4+
from llvmlite import ir
5+
from dataclasses import dataclass
6+
from typing import Any
7+
from pythonbpf.helper import HelperHandlerRegistry
8+
from pythonbpf.type_deducer import ctypes_to_ir
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
@dataclass
14+
class LocalSymbol:
15+
var: ir.AllocaInstr
16+
ir_type: ir.Type
17+
metadata: Any = None
18+
19+
def __iter__(self):
20+
yield self.var
21+
yield self.ir_type
22+
yield self.metadata
23+
24+
25+
def _is_helper_call(call_node):
26+
"""Check if a call node is a BPF helper function call."""
27+
if isinstance(call_node.func, ast.Name):
28+
# Exclude print from requiring temps (handles f-strings differently)
29+
func_name = call_node.func.id
30+
return HelperHandlerRegistry.has_handler(func_name) and func_name != "print"
31+
32+
elif isinstance(call_node.func, ast.Attribute):
33+
return HelperHandlerRegistry.has_handler(call_node.func.attr)
34+
35+
return False
36+
37+
38+
def handle_assign_allocation(builder, stmt, local_sym_tab, structs_sym_tab):
39+
"""Handle memory allocation for assignment statements."""
40+
41+
# Validate assignment
42+
if len(stmt.targets) != 1:
43+
logger.warning("Multi-target assignment not supported, skipping allocation")
44+
return
45+
46+
target = stmt.targets[0]
47+
48+
# Skip non-name targets (e.g., struct field assignments)
49+
if isinstance(target, ast.Attribute):
50+
logger.debug(f"Struct field assignment to {target.attr}, no allocation needed")
51+
return
52+
53+
if not isinstance(target, ast.Name):
54+
logger.warning(f"Unsupported assignment target type: {type(target).__name__}")
55+
return
56+
57+
var_name = target.id
58+
rval = stmt.value
59+
60+
# Skip if already allocated
61+
if var_name in local_sym_tab:
62+
logger.debug(f"Variable {var_name} already allocated, skipping")
63+
return
64+
65+
# Determine type and allocate based on rval
66+
if isinstance(rval, ast.Call):
67+
_allocate_for_call(builder, var_name, rval, local_sym_tab, structs_sym_tab)
68+
elif isinstance(rval, ast.Constant):
69+
_allocate_for_constant(builder, var_name, rval, local_sym_tab)
70+
elif isinstance(rval, ast.BinOp):
71+
_allocate_for_binop(builder, var_name, local_sym_tab)
72+
else:
73+
logger.warning(
74+
f"Unsupported assignment value type for {var_name}: {type(rval).__name__}"
75+
)
76+
77+
78+
def _allocate_for_call(builder, var_name, rval, local_sym_tab, structs_sym_tab):
79+
"""Allocate memory for variable assigned from a call."""
80+
81+
if isinstance(rval.func, ast.Name):
82+
call_type = rval.func.id
83+
84+
# C type constructors
85+
if call_type in ("c_int32", "c_int64", "c_uint32", "c_uint64"):
86+
ir_type = ctypes_to_ir(call_type)
87+
var = builder.alloca(ir_type, name=var_name)
88+
var.align = ir_type.width // 8
89+
local_sym_tab[var_name] = LocalSymbol(var, ir_type)
90+
logger.info(f"Pre-allocated {var_name} as {call_type}")
91+
92+
# Helper functions
93+
elif HelperHandlerRegistry.has_handler(call_type):
94+
ir_type = ir.IntType(64) # Assume i64 return type
95+
var = builder.alloca(ir_type, name=var_name)
96+
var.align = 8
97+
local_sym_tab[var_name] = LocalSymbol(var, ir_type)
98+
logger.info(f"Pre-allocated {var_name} for helper {call_type}")
99+
100+
# Deref function
101+
elif call_type == "deref":
102+
ir_type = ir.IntType(64) # Assume i64 return type
103+
var = builder.alloca(ir_type, name=var_name)
104+
var.align = 8
105+
local_sym_tab[var_name] = LocalSymbol(var, ir_type)
106+
logger.info(f"Pre-allocated {var_name} for deref")
107+
108+
# Struct constructors
109+
elif call_type in structs_sym_tab:
110+
struct_info = structs_sym_tab[call_type]
111+
var = builder.alloca(struct_info.ir_type, name=var_name)
112+
local_sym_tab[var_name] = LocalSymbol(var, struct_info.ir_type, call_type)
113+
logger.info(f"Pre-allocated {var_name} for struct {call_type}")
114+
115+
else:
116+
logger.warning(f"Unknown call type for allocation: {call_type}")
117+
118+
elif isinstance(rval.func, ast.Attribute):
119+
# Map method calls - need double allocation for ptr handling
120+
_allocate_for_map_method(builder, var_name, local_sym_tab)
121+
122+
else:
123+
logger.warning(f"Unsupported call function type for {var_name}")
124+
125+
126+
def _allocate_for_map_method(builder, var_name, local_sym_tab):
127+
"""Allocate memory for variable assigned from map method (double alloc)."""
128+
129+
# Main variable (pointer to pointer)
130+
ir_type = ir.PointerType(ir.IntType(64))
131+
var = builder.alloca(ir_type, name=var_name)
132+
local_sym_tab[var_name] = LocalSymbol(var, ir_type)
133+
134+
# Temporary variable for computed values
135+
tmp_ir_type = ir.IntType(64)
136+
var_tmp = builder.alloca(tmp_ir_type, name=f"{var_name}_tmp")
137+
local_sym_tab[f"{var_name}_tmp"] = LocalSymbol(var_tmp, tmp_ir_type)
138+
139+
logger.info(f"Pre-allocated {var_name} and {var_name}_tmp for map method")
140+
141+
142+
def _allocate_for_constant(builder, var_name, rval, local_sym_tab):
143+
"""Allocate memory for variable assigned from a constant."""
144+
145+
if isinstance(rval.value, bool):
146+
ir_type = ir.IntType(1)
147+
var = builder.alloca(ir_type, name=var_name)
148+
var.align = 1
149+
local_sym_tab[var_name] = LocalSymbol(var, ir_type)
150+
logger.info(f"Pre-allocated {var_name} as bool")
151+
152+
elif isinstance(rval.value, int):
153+
ir_type = ir.IntType(64)
154+
var = builder.alloca(ir_type, name=var_name)
155+
var.align = 8
156+
local_sym_tab[var_name] = LocalSymbol(var, ir_type)
157+
logger.info(f"Pre-allocated {var_name} as i64")
158+
159+
elif isinstance(rval.value, str):
160+
ir_type = ir.PointerType(ir.IntType(8))
161+
var = builder.alloca(ir_type, name=var_name)
162+
var.align = 8
163+
local_sym_tab[var_name] = LocalSymbol(var, ir_type)
164+
logger.info(f"Pre-allocated {var_name} as string")
165+
166+
else:
167+
logger.warning(
168+
f"Unsupported constant type for {var_name}: {type(rval.value).__name__}"
169+
)
170+
171+
172+
def _allocate_for_binop(builder, var_name, local_sym_tab):
173+
"""Allocate memory for variable assigned from a binary operation."""
174+
ir_type = ir.IntType(64) # Assume i64 result
175+
var = builder.alloca(ir_type, name=var_name)
176+
var.align = 8
177+
local_sym_tab[var_name] = LocalSymbol(var, ir_type)
178+
logger.info(f"Pre-allocated {var_name} for binop result")
179+
180+
181+
def allocate_temp_pool(builder, max_temps, local_sym_tab):
182+
"""Allocate the temporary scratch space pool for helper arguments."""
183+
if max_temps == 0:
184+
return
185+
186+
logger.info(f"Allocating temp pool of {max_temps} variables")
187+
for i in range(max_temps):
188+
temp_name = f"__helper_temp_{i}"
189+
temp_var = builder.alloca(ir.IntType(64), name=temp_name)
190+
temp_var.align = 8
191+
local_sym_tab[temp_name] = LocalSymbol(temp_var, ir.IntType(64))

0 commit comments

Comments
 (0)