Skip to content

Commit ab1c422

Browse files
fix broken IR generation logic for globals
1 parent c3a512d commit ab1c422

File tree

2 files changed

+65
-28
lines changed

2 files changed

+65
-28
lines changed

pythonbpf/globals_pass.py

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
from llvmlite import ir
22
import ast
33

4-
from llvmlite import ir
5-
import ast
64
from logging import Logger
75
import logging
86
from .type_deducer import ctypes_to_ir
@@ -11,11 +9,41 @@
119

1210

1311
def emit_global(module: ir.Module, node, name):
14-
print("global", node.returns.id)
12+
logger.info(f"global identifier {name} processing")
13+
# TODO: below part is LLM generated check logic.
14+
# deduce LLVM type from the annotated return
15+
if not isinstance(node.returns, ast.Name):
16+
raise ValueError(f"Unsupported return annotation {ast.dump(node.returns)}")
1517
ty = ctypes_to_ir(node.returns.id)
1618

19+
# extract the return expression
20+
ret_stmt = node.body[0]
21+
if not isinstance(ret_stmt, ast.Return) or ret_stmt.value is None:
22+
raise ValueError(f"Global '{name}' has no valid return")
23+
24+
init_val = ret_stmt.value
25+
26+
# simple constant like "return 0"
27+
if isinstance(init_val, ast.Constant):
28+
llvm_init = ir.Constant(ty, init_val.value)
29+
30+
# variable reference like "return SOME_CONST"
31+
elif isinstance(init_val, ast.Name):
32+
# you may need symbol resolution here, stub as 0 for now
33+
raise ValueError(f"Name reference {init_val.id} not yet supported")
34+
35+
# constructor call like "return c_int64(0)" or dataclass(...)
36+
elif isinstance(init_val, ast.Call):
37+
if len(init_val.args) == 1 and isinstance(init_val.args[0], ast.Constant):
38+
llvm_init = ir.Constant(ty, init_val.args[0].value)
39+
else:
40+
raise ValueError(f"Complex constructor not supported: {ast.dump(init_val)}")
41+
42+
else:
43+
raise ValueError(f"Unsupported return expr {ast.dump(init_val)}")
44+
1745
gvar = ir.GlobalVariable(module, ty, name=name)
18-
gvar.initializer = ir.Constant(ty, initial_value)
46+
gvar.initializer = llvm_init
1947
gvar.align = 8
2048
gvar.linkage = "dso_local"
2149
gvar.global_constant = False
@@ -24,11 +52,11 @@ def emit_global(module: ir.Module, node, name):
2452

2553
def globals_processing(tree, module):
2654
"""Process stuff decorated with @bpf and @bpfglobal except license and return the section name"""
27-
global_sym_tab = []
55+
globals_sym_tab = []
2856

2957
for node in tree.body:
3058
# Skip non-assignment and non-function nodes
31-
if not (isinstance(node, (ast.FunctionDef, ast.AnnAssign, ast.Assign))):
59+
if not (isinstance(node, ast.FunctionDef)):
3260
continue
3361

3462
# Get the name based on node type
@@ -38,33 +66,31 @@ def globals_processing(tree, module):
3866
continue
3967

4068
# Check for duplicate names
41-
if name in global_sym_tab:
69+
if name in globals_sym_tab:
4270
raise SyntaxError(f"ERROR: Global name '{name}' previously defined")
4371
else:
44-
global_sym_tab.append(name)
72+
globals_sym_tab.append(name)
4573

46-
# Process decorated functions
4774
if isinstance(node, ast.FunctionDef) and node.name != "LICENSE":
48-
# Check decorators
4975
decorators = [
5076
dec.id for dec in node.decorator_list if isinstance(dec, ast.Name)
5177
]
52-
5378
if "bpf" in decorators and "bpfglobal" in decorators:
5479
if (
55-
len(node.body) == 1
56-
and isinstance(node.body[0], ast.Return)
57-
and node.body[0].value is not None
58-
and isinstance(node.body[0].value, (ast.Constant, ast.Name))
80+
len(node.body) == 1
81+
and isinstance(node.body[0], ast.Return)
82+
and node.body[0].value is not None
83+
and isinstance(
84+
node.body[0].value, (ast.Constant, ast.Name, ast.Call)
85+
)
5986
):
6087
emit_global(module, node, name)
61-
return node.name
6288
else:
63-
logger.info(f"Invalid global expression for '{node.name}'")
64-
return None
89+
raise SyntaxError(f"ERROR: Invalid syntax for {name} global")
6590

6691
return None
6792

93+
6894
def emit_llvm_compiler_used(module: ir.Module, names: list[str]):
6995
"""
7096
Emit the @llvm.compiler.used global given a list of function/global names.
@@ -94,12 +120,12 @@ def globals_list_creation(tree, module: ir.Module):
94120
if isinstance(node, ast.FunctionDef):
95121
for dec in node.decorator_list:
96122
if (
97-
isinstance(dec, ast.Call)
98-
and isinstance(dec.func, ast.Name)
99-
and dec.func.id == "section"
100-
and len(dec.args) == 1
101-
and isinstance(dec.args[0], ast.Constant)
102-
and isinstance(dec.args[0].value, str)
123+
isinstance(dec, ast.Call)
124+
and isinstance(dec.func, ast.Name)
125+
and dec.func.id == "section"
126+
and len(dec.args) == 1
127+
and isinstance(dec.args[0], ast.Constant)
128+
and isinstance(dec.args[0].value, str)
103129
):
104130
collected.append(node.name)
105131

tests/failing_tests/globals.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,28 @@
11
import logging
22

33
from pythonbpf import compile, bpf, section, bpfglobal, compile_to_ir
4-
from ctypes import c_void_p, c_int64
4+
from ctypes import c_void_p, c_int64, c_int32
55

66
@bpf
77
@bpfglobal
8-
def somevalue() -> c_int64:
8+
def somevalue() -> c_int32:
9+
return c_int32(0)
10+
11+
@bpf
12+
@bpfglobal
13+
def somevalue2() -> c_int64:
914
return c_int64(0)
1015

1116
@bpf
12-
@section("sometag1")
17+
@bpfglobal
18+
def somevalue1() -> c_int32:
19+
return c_int32(0)
20+
21+
@bpf
22+
@section("tracepoint/syscalls/sys_enter_execve")
1323
def sometag(ctx: c_void_p) -> c_int64:
14-
return c_int64(0)
24+
print("test")
25+
return c_int64(1)
1526

1627
@bpf
1728
@bpfglobal

0 commit comments

Comments
 (0)