From be1d86da746ab02bd050d797b20b141d4843fbd0 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Fri, 18 Sep 2026 13:41:22 +0530 Subject: [PATCH 1/2] Core: Let a local be initialised from a vmlinux enum constant Every read of a bare name resolves local, then BPF global, then vmlinux enum constant, except the allocation pass's variable-to-variable path, which stopped after the globals and rejected `act = XDP_PASS` with "source variable not found", while `act = XDP_PASS + 0` compiled through the binop path. The error text pointed the wrong way: it is the target that needs a stack slot, and the pass could not type it. An enum constant is an immediate with no storage of its own, so the target gets the i64 slot a literal gets, and the constant is folded at the store. Co-Authored-By: Claude Fable 5.1 --- pythonbpf/allocation_pass.py | 11 +++++++++-- .../vmlinux/assignment_handling.py | 0 2 files changed, 9 insertions(+), 2 deletions(-) rename tests/{failing_tests => passing_tests}/vmlinux/assignment_handling.py (100%) diff --git a/pythonbpf/allocation_pass.py b/pythonbpf/allocation_pass.py index 456c1e4c..b2098e0f 100644 --- a/pythonbpf/allocation_pass.py +++ b/pythonbpf/allocation_pass.py @@ -324,12 +324,19 @@ def _allocate_for_name(builder, var_name, rval, local_sym_tab, compilation_conte """Allocate memory for variable-to-variable assignment (b = a).""" source_var = rval.id - # Local first, then a BPF global: the copy takes the source's type either - # way (a c_uint32 global gives a c_uint32 local). + # Same resolution order as every other read of a bare name: local, then + # BPF global, then vmlinux enum constant. A variable source gives the copy + # its type; an enum constant is an immediate with no storage, so the copy + # gets the i64 slot a literal gets. if source_var in local_sym_tab: source_symbol = local_sym_tab[source_var] elif source_var in compilation_context.bpf_globals: source_symbol = compilation_context.bpf_globals[source_var] + elif VmlinuxHandlerRegistry.handle_name(source_var) is not None: + var = _allocate_with_type(builder, var_name, ir.IntType(64)) + local_sym_tab[var_name] = LocalSymbol(var, ir.IntType(64)) + logger.info(f"Pre-allocated {var_name} from enum constant {source_var}") + return else: logger.error(f"Source variable '{source_var}' not found in symbol table") return diff --git a/tests/failing_tests/vmlinux/assignment_handling.py b/tests/passing_tests/vmlinux/assignment_handling.py similarity index 100% rename from tests/failing_tests/vmlinux/assignment_handling.py rename to tests/passing_tests/vmlinux/assignment_handling.py From 51d8628ae23427a341203d5a9c10693291c6aa35 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Fri, 18 Sep 2026 13:41:26 +0530 Subject: [PATCH 2/2] Tests: Enum-constant locals pass now failing_tests/vmlinux/assignment_handling.py pinned exactly this gap ("This gives an error, but / this does not"); it moves to the passing suite and its xfail entry goes. enum_to_local.py adds the C shape `int act = XDP_PASS; if (...) act = XDP_DROP; return act;`. Co-Authored-By: Claude Fable 5.1 --- .../vmlinux/assignment_handling.py | 4 +-- tests/passing_tests/vmlinux/enum_to_local.py | 26 +++++++++++++++++++ tests/test_config.toml | 1 - 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 tests/passing_tests/vmlinux/enum_to_local.py diff --git a/tests/passing_tests/vmlinux/assignment_handling.py b/tests/passing_tests/vmlinux/assignment_handling.py index b8fe43ee..7e59075a 100644 --- a/tests/passing_tests/vmlinux/assignment_handling.py +++ b/tests/passing_tests/vmlinux/assignment_handling.py @@ -7,8 +7,8 @@ @bpf @section("kprobe/blk_mq_start_request") def example(ctx: c_void_p) -> c_int64: - d = XDP_PASS # This gives an error, but - e = XDP_PASS + 0 # this does not + d = XDP_PASS # bare enum constant: used to be rejected at allocation + e = XDP_PASS + 0 # through a binop: always worked; the two must agree print(f"test1 {e} test2 {d}") return c_int64(0) diff --git a/tests/passing_tests/vmlinux/enum_to_local.py b/tests/passing_tests/vmlinux/enum_to_local.py new file mode 100644 index 00000000..42a2daa6 --- /dev/null +++ b/tests/passing_tests/vmlinux/enum_to_local.py @@ -0,0 +1,26 @@ +# A local initialised from a vmlinux enum constant, the C shape +# `int act = XDP_PASS;`. Reads of a bare name resolve local, then BPF global, +# then vmlinux enum everywhere else; the allocation pass used to stop one step +# short for this shape and reject it, while `act = XDP_PASS + 0` compiled. +from ctypes import c_int64 +from pythonbpf import bpf, section, bpfglobal, compile +from vmlinux import struct_xdp_md +from vmlinux import XDP_PASS, XDP_DROP + + +@bpf +@section("xdp") +def prog(ctx: struct_xdp_md) -> c_int64: + act = XDP_PASS + if ctx.data_end - ctx.data > 1500: + act = XDP_DROP + return c_int64(act) + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() diff --git a/tests/test_config.toml b/tests/test_config.toml index 7e244020..cab8db6a 100644 --- a/tests/test_config.toml +++ b/tests/test_config.toml @@ -29,7 +29,6 @@ "failing_tests/vmlinux/args_test.py" = {reason = "struct_trace_event_raw_sys_enter args field access not supported", level = "ir"} -"failing_tests/vmlinux/assignment_handling.py" = {reason = "Assigning vmlinux enum value (XDP_PASS) to a local variable not yet supported", level = "ir"} "failing_tests/xdp_pass.py" = {reason = "XDP program using vmlinux structs (struct_xdp_md) and complex map/struct interaction not yet supported", level = "ir"}