Skip to content

Commit eaaa12e

Browse files
r41k0uclaude
andcommitted
Tests: Return of a map value, return of a struct, enum-constant rank
return/map_value.py is the C shape `if (p) return *p;`; return_struct.py is the type error convert() now raises; vmlinux/enum_rank.py pins the u32 trunc/zext pair for `XDP_PASS - k`. test_signedness_ir.py takes paths under passing_tests so it can assert on all three, and skips the vmlinux case when vmlinux.py is not importable, as the harness does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent ca92f2a commit eaaa12e

5 files changed

Lines changed: 113 additions & 11 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# A struct value has no integer to reach by dereferencing, so returning one
2+
# from an integer function is a type error, raised by convert() with both
3+
# types named rather than left for llc to reject with an IR line number.
4+
from pythonbpf import bpf, struct, section, bpfglobal, compile
5+
from ctypes import c_void_p, c_int64, c_uint64
6+
7+
8+
@bpf
9+
@struct
10+
class task_info:
11+
pid: c_uint64
12+
13+
14+
@bpf
15+
@section("tracepoint/raw_syscalls/sys_enter")
16+
def prog(ctx: c_void_p) -> c_int64:
17+
t = task_info()
18+
t.pid = 1
19+
return t
20+
21+
22+
@bpf
23+
@bpfglobal
24+
def LICENSE() -> str:
25+
return "GPL"
26+
27+
28+
compile()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# `return` is a consumer of a value like any other: a pointer to one is
2+
# dereferenced (null-checked) to reach it, so returning a map lookup result
3+
# works the way `p + 0` already did. C shape: `if (p) return *p; return 0;`
4+
from pythonbpf import bpf, map, section, bpfglobal, compile
5+
from pythonbpf.maps import HashMap
6+
from ctypes import c_void_p, c_int64, c_uint32, c_uint64
7+
8+
9+
@bpf
10+
@map
11+
def m() -> HashMap:
12+
return HashMap(key=c_uint32, value=c_uint64, max_entries=4)
13+
14+
15+
@bpf
16+
@section("tracepoint/raw_syscalls/sys_enter")
17+
def prog(ctx: c_void_p) -> c_int64:
18+
k = c_uint32(1)
19+
p = m.lookup(k)
20+
if p:
21+
return p
22+
return c_int64(0)
23+
24+
25+
@bpf
26+
@bpfglobal
27+
def LICENSE() -> str:
28+
return "GPL"
29+
30+
31+
compile()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# An enum constant has C's `int` type, whatever the enum's underlying type is
2+
# (clang: `XDP_PASS - k` is `sub nsw i32` + sext; a *variable* of the enum
3+
# type would be u32). So `XDP_PASS - k` with an unsigned k is a u32 operation,
4+
# the same rank a literal that fits in int gets.
5+
from ctypes import c_int64, c_uint32, c_void_p
6+
from pythonbpf import bpf, section, bpfglobal, compile
7+
from vmlinux import XDP_PASS
8+
9+
10+
@bpf
11+
@section("tracepoint/raw_syscalls/sys_enter")
12+
def prog(ctx: c_void_p) -> c_int64:
13+
k = c_uint32(3)
14+
d = XDP_PASS - k
15+
return c_int64(d)
16+
17+
18+
@bpf
19+
@bpfglobal
20+
def LICENSE() -> str:
21+
return "GPL"
22+
23+
24+
compile()

‎tests/test_config.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
# in a form the verifier can follow.
2525
"failing_tests/xdp/xdp_test_1.py" = {reason = "XDP direct packet access: the data/data_end guard does not establish a packet range the verifier can use", level = "verifier"}
2626

27+
"failing_tests/return_struct.py" = {reason = "Returning a struct value from an integer function is a type error (nothing to dereference to an integer)", level = "ir"}
28+
2729
"failing_tests/license.py" = {reason = "Missing LICENSE global produces IR that llc rejects — should be caught earlier with a clear error message", level = "llc"}
2830

2931
"failing_tests/undeclared_values.py" = {reason = "Undeclared variable used in f-string — should raise SyntaxError (correct behaviour, test documents it)", level = "ir"}

‎tests/test_signedness_ir.py‎

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,53 +8,70 @@
88
icmp ugt vs sgt, lshr vs ashr, and the trunc/zext pair that wraps u32 * u32.
99
"""
1010

11+
import importlib.util
1112
import re
1213
from pathlib import Path
1314

1415
import pytest
1516

1617
from tests.framework.compiler import run_ir_generation
1718

18-
SIGNEDNESS_DIR = Path(__file__).parent / "passing_tests" / "signedness"
19+
PASSING_DIR = Path(__file__).parent / "passing_tests"
20+
HAVE_VMLINUX = importlib.util.find_spec("vmlinux") is not None
1921

20-
# file -> (patterns that must appear, patterns that must not)
22+
# path under passing_tests -> (patterns that must appear, patterns that must not)
2123
CASES = {
22-
"widen_unsigned.py": (
24+
"signedness/widen_unsigned.py": (
2325
[r"zext i32 .* to i64"],
2426
[r"sext i32 .* to i64"],
2527
),
26-
"widen_signed.py": (
28+
"signedness/widen_signed.py": (
2729
[r"sext i32 .* to i64"],
2830
[r"zext i32 .* to i64"],
2931
),
30-
"mixed_division.py": (
32+
"signedness/mixed_division.py": (
3133
[r"\budiv i64", r"\burem i64", r"\bsdiv i64"],
3234
[r"\bsrem i64"],
3335
),
34-
"unsigned_compare.py": (
36+
"signedness/unsigned_compare.py": (
3537
[r"icmp ugt i64", r"icmp sgt i64"],
3638
[],
3739
),
38-
"narrow_wrap.py": (
40+
"signedness/narrow_wrap.py": (
3941
# the product is computed at 64 bits, cut to 32, then zero-extended
4042
[r"\bmul i64", r"trunc i64 .* to i32", r"zext i32 .* to i64"],
4143
[r"sext i32 .* to i64"],
4244
),
43-
"right_shift.py": (
45+
"signedness/right_shift.py": (
4446
[r"\blshr i64 .*, 4", r"\bashr i64 .*, 4"],
4547
[],
4648
),
47-
"literal_rank.py": (
49+
"signedness/literal_rank.py": (
4850
[r"\budiv i64 .*, 4294967294"],
4951
[r"\bsdiv i64"],
5052
),
53+
# An enum constant is a C `int`, so `XDP_PASS - k` with k a c_uint32 is a
54+
# u32 operation: the result is cut to 32 bits and zero-extended. Ranked
55+
# as i64 it would be a signed 64-bit subtraction with no trunc at all.
56+
"vmlinux/enum_rank.py": (
57+
[r"trunc i64 .* to i32", r"zext i32 .* to i64"],
58+
[r"sext i32 .* to i64"],
59+
),
60+
# `return p` on a map lookup dereferences through a null check and returns
61+
# the i64, never the pointer.
62+
"return/map_value.py": (
63+
[r"deref_0_not_null", r"ret i64 %"],
64+
[r"ret i64\*"],
65+
),
5166
}
5267

5368

5469
@pytest.mark.parametrize("name", list(CASES))
5570
def test_signedness_ir_shape(name, tmp_path):
56-
ll_path = tmp_path / name.replace(".py", ".ll")
57-
run_ir_generation(SIGNEDNESS_DIR / name, ll_path)
71+
if name.startswith("vmlinux/") and not HAVE_VMLINUX:
72+
pytest.skip("vmlinux.py not importable")
73+
ll_path = tmp_path / Path(name).name.replace(".py", ".ll")
74+
run_ir_generation(PASSING_DIR / name, ll_path)
5875
ir_text = ll_path.read_text()
5976

6077
expected, forbidden = CASES[name]

0 commit comments

Comments
 (0)