From dc7a127fa6ae6f5aef543ae4d943b78c30c29ef4 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Sun, 5 Oct 2025 15:09:15 +0530 Subject: [PATCH 01/24] Restructure dir for functions --- pythonbpf/codegen.py | 2 +- pythonbpf/functions/__init__.py | 3 +++ pythonbpf/{ => functions}/functions_pass.py | 8 ++++---- 3 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 pythonbpf/functions/__init__.py rename pythonbpf/{ => functions}/functions_pass.py (99%) diff --git a/pythonbpf/codegen.py b/pythonbpf/codegen.py index cf20f06..1c9b9dd 100644 --- a/pythonbpf/codegen.py +++ b/pythonbpf/codegen.py @@ -1,7 +1,7 @@ import ast from llvmlite import ir from .license_pass import license_processing -from .functions_pass import func_proc +from .functions import func_proc from .maps import maps_proc from .structs import structs_proc from .globals_pass import globals_processing diff --git a/pythonbpf/functions/__init__.py b/pythonbpf/functions/__init__.py new file mode 100644 index 0000000..df99da1 --- /dev/null +++ b/pythonbpf/functions/__init__.py @@ -0,0 +1,3 @@ +from .functions_pass import func_proc + +__all__ = ["func_proc"] diff --git a/pythonbpf/functions_pass.py b/pythonbpf/functions/functions_pass.py similarity index 99% rename from pythonbpf/functions_pass.py rename to pythonbpf/functions/functions_pass.py index d1ea526..7766a9d 100644 --- a/pythonbpf/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -4,10 +4,10 @@ from typing import Any from dataclasses import dataclass -from .helper import HelperHandlerRegistry, handle_helper_call -from .type_deducer import ctypes_to_ir -from .binary_ops import handle_binary_op -from .expr_pass import eval_expr, handle_expr +from pythonbpf.helper import HelperHandlerRegistry, handle_helper_call +from pythonbpf.type_deducer import ctypes_to_ir +from pythonbpf.binary_ops import handle_binary_op +from pythonbpf.expr_pass import eval_expr, handle_expr logger = logging.getLogger(__name__) From ceaac786335b5d48bcf7e125ae67b7f7656f0a50 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Sun, 5 Oct 2025 15:12:01 +0530 Subject: [PATCH 02/24] Janitorial: fix lint --- pythonbpf/functions/functions_pass.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 7766a9d..c6a15a9 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -410,7 +410,7 @@ def process_stmt( raise ValueError("Failed to evaluate return expression") if val[1] != ret_type: raise ValueError( - "Return type mismatch: expected " f"{ret_type}, got {val[1]}" + f"Return type mismatch: expected {ret_type}, got {val[1]}" ) builder.ret(val[0]) did_return = True @@ -420,8 +420,7 @@ def process_stmt( val = builder.load(var) if val.type != ret_type: raise ValueError( - "Return type mismatch: expected" - f"{ret_type}, got {val.type}" + f"Return type mismatch: expected {ret_type}, got {val.type}" ) builder.ret(val) did_return = True From b09dc815fc76e7952b6f05c60e9b8c20c81f5c00 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Sun, 5 Oct 2025 15:19:16 +0530 Subject: [PATCH 03/24] Add StatementHandlerRegistry --- pythonbpf/functions/func_registry_handlers.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 pythonbpf/functions/func_registry_handlers.py diff --git a/pythonbpf/functions/func_registry_handlers.py b/pythonbpf/functions/func_registry_handlers.py new file mode 100644 index 0000000..010e3ed --- /dev/null +++ b/pythonbpf/functions/func_registry_handlers.py @@ -0,0 +1,22 @@ +from typing import Dict + + +class StatementHandlerRegistry: + """Registry for statement handlers.""" + + _handlers: Dict = {} + + @classmethod + def register(cls, stmt_type): + """Register a handler for a specific statement type.""" + + def decorator(handler): + cls._handlers[stmt_type] = handler + return handler + + return decorator + + @classmethod + def get_handler(cls, stmt_type): + """Get the handler for a specific statement type.""" + return cls._handlers.get(stmt_type, None) From d0a8e96b702ef9f7f685047c8e414a3ba3a371d1 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Sun, 5 Oct 2025 20:10:07 +0530 Subject: [PATCH 04/24] Use getitem dunder for StatementHandlerRegistry --- pythonbpf/functions/func_registry_handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonbpf/functions/func_registry_handlers.py b/pythonbpf/functions/func_registry_handlers.py index 010e3ed..afe54f6 100644 --- a/pythonbpf/functions/func_registry_handlers.py +++ b/pythonbpf/functions/func_registry_handlers.py @@ -17,6 +17,6 @@ def decorator(handler): return decorator @classmethod - def get_handler(cls, stmt_type): + def __getitem__(cls, stmt_type): """Get the handler for a specific statement type.""" return cls._handlers.get(stmt_type, None) From e9f3aa25d27c475ba4e93062671df438e2ce4fc0 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Sun, 5 Oct 2025 23:19:06 +0530 Subject: [PATCH 05/24] Make handle_return (crude for now) --- pythonbpf/functions/functions_pass.py | 124 +++++++++++++++----------- 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index c6a15a9..5143a93 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -9,6 +9,7 @@ from pythonbpf.binary_ops import handle_binary_op from pythonbpf.expr_pass import eval_expr, handle_expr + logger = logging.getLogger(__name__) @@ -350,6 +351,65 @@ def handle_if( builder.position_at_end(merge_block) +def handle_return( + func, module, builder, stmt, map_sym_tab, local_sym_tab, struct_sym_tab, ret_type +): + if stmt.value is None: + builder.ret(ir.Constant(ir.IntType(64), 0)) + return True + elif ( + isinstance(stmt.value, ast.Call) + and isinstance(stmt.value.func, ast.Name) + and len(stmt.value.args) == 1 + ): + if isinstance(stmt.value.args[0], ast.Constant) and isinstance( + stmt.value.args[0].value, int + ): + call_type = stmt.value.func.id + if ctypes_to_ir(call_type) != ret_type: + raise ValueError( + "Return type mismatch: expected" + f"{ctypes_to_ir(call_type)}, got {call_type}" + ) + else: + builder.ret(ir.Constant(ret_type, stmt.value.args[0].value)) + return True + elif isinstance(stmt.value.args[0], ast.BinOp): + # TODO: Should be routed through eval_expr + val = handle_binary_op(stmt.value.args[0], builder, None, local_sym_tab) + if val is None: + raise ValueError("Failed to evaluate return expression") + if val[1] != ret_type: + raise ValueError( + f"Return type mismatch: expected {ret_type}, got {val[1]}" + ) + builder.ret(val[0]) + return True + elif isinstance(stmt.value.args[0], ast.Name): + if stmt.value.args[0].id in local_sym_tab: + var = local_sym_tab[stmt.value.args[0].id].var + val = builder.load(var) + if val.type != ret_type: + raise ValueError( + f"Return type mismatch: expected {ret_type}, got {val.type}" + ) + builder.ret(val) + return True + else: + raise ValueError("Failed to evaluate return expression") + elif isinstance(stmt.value, ast.Name): + if stmt.value.id == "XDP_PASS": + builder.ret(ir.Constant(ret_type, 2)) + return True + elif stmt.value.id == "XDP_DROP": + builder.ret(ir.Constant(ret_type, 1)) + return True + else: + raise ValueError("Failed to evaluate return expression") + else: + raise ValueError("Unsupported return value") + + def process_stmt( func, module, @@ -383,60 +443,16 @@ def process_stmt( func, module, builder, stmt, map_sym_tab, local_sym_tab, structs_sym_tab ) elif isinstance(stmt, ast.Return): - if stmt.value is None: - builder.ret(ir.Constant(ir.IntType(64), 0)) - did_return = True - elif ( - isinstance(stmt.value, ast.Call) - and isinstance(stmt.value.func, ast.Name) - and len(stmt.value.args) == 1 - ): - if isinstance(stmt.value.args[0], ast.Constant) and isinstance( - stmt.value.args[0].value, int - ): - call_type = stmt.value.func.id - if ctypes_to_ir(call_type) != ret_type: - raise ValueError( - "Return type mismatch: expected" - f"{ctypes_to_ir(call_type)}, got {call_type}" - ) - else: - builder.ret(ir.Constant(ret_type, stmt.value.args[0].value)) - did_return = True - elif isinstance(stmt.value.args[0], ast.BinOp): - # TODO: Should be routed through eval_expr - val = handle_binary_op(stmt.value.args[0], builder, None, local_sym_tab) - if val is None: - raise ValueError("Failed to evaluate return expression") - if val[1] != ret_type: - raise ValueError( - f"Return type mismatch: expected {ret_type}, got {val[1]}" - ) - builder.ret(val[0]) - did_return = True - elif isinstance(stmt.value.args[0], ast.Name): - if stmt.value.args[0].id in local_sym_tab: - var = local_sym_tab[stmt.value.args[0].id].var - val = builder.load(var) - if val.type != ret_type: - raise ValueError( - f"Return type mismatch: expected {ret_type}, got {val.type}" - ) - builder.ret(val) - did_return = True - else: - raise ValueError("Failed to evaluate return expression") - elif isinstance(stmt.value, ast.Name): - if stmt.value.id == "XDP_PASS": - builder.ret(ir.Constant(ret_type, 2)) - did_return = True - elif stmt.value.id == "XDP_DROP": - builder.ret(ir.Constant(ret_type, 1)) - did_return = True - else: - raise ValueError("Failed to evaluate return expression") - else: - raise ValueError("Unsupported return value") + did_return = handle_return( + func, + module, + builder, + stmt, + map_sym_tab, + local_sym_tab, + structs_sym_tab, + ret_type, + ) return did_return From f96a6b94dc9e693cc0ac0ace4fbec814302985ed Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Sun, 5 Oct 2025 23:40:48 +0530 Subject: [PATCH 06/24] Remove useless args from handle_return --- pythonbpf/functions/functions_pass.py | 8 +------- pythonbpf/functions/return_utils.py | 0 2 files changed, 1 insertion(+), 7 deletions(-) create mode 100644 pythonbpf/functions/return_utils.py diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 5143a93..64b9233 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -351,9 +351,7 @@ def handle_if( builder.position_at_end(merge_block) -def handle_return( - func, module, builder, stmt, map_sym_tab, local_sym_tab, struct_sym_tab, ret_type -): +def handle_return(builder, stmt, local_sym_tab, ret_type): if stmt.value is None: builder.ret(ir.Constant(ir.IntType(64), 0)) return True @@ -444,13 +442,9 @@ def process_stmt( ) elif isinstance(stmt, ast.Return): did_return = handle_return( - func, - module, builder, stmt, - map_sym_tab, local_sym_tab, - structs_sym_tab, ret_type, ) return did_return diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py new file mode 100644 index 0000000..e69de29 From a21ff5633c06b5ea6670c97dd6a77ba664daee3c Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Sun, 5 Oct 2025 23:44:46 +0530 Subject: [PATCH 07/24] Add _handle_none_return --- pythonbpf/functions/functions_pass.py | 5 +++-- pythonbpf/functions/return_utils.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 64b9233..6eff2e7 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -9,6 +9,8 @@ from pythonbpf.binary_ops import handle_binary_op from pythonbpf.expr_pass import eval_expr, handle_expr +from .return_utils import _handle_none_return + logger = logging.getLogger(__name__) @@ -353,8 +355,7 @@ def handle_if( def handle_return(builder, stmt, local_sym_tab, ret_type): if stmt.value is None: - builder.ret(ir.Constant(ir.IntType(64), 0)) - return True + return _handle_none_return(builder) elif ( isinstance(stmt.value, ast.Call) and isinstance(stmt.value.func, ast.Name) diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py index e69de29..12a2d01 100644 --- a/pythonbpf/functions/return_utils.py +++ b/pythonbpf/functions/return_utils.py @@ -0,0 +1,11 @@ +import logging +import ir + +logger: logging.Logger = logging.getLogger(__name__) + + +def _handle_none_return(builder) -> bool: + """Handle return or return None -> returns 0.""" + builder.ret(ir.Constant(ir.IntType(64), 0)) + logger.debug("Generated default return: 0") + return True From 6f02b61527ec30c8d60e0b8d0d035c04a3f22a61 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Sun, 5 Oct 2025 23:54:06 +0530 Subject: [PATCH 08/24] Add _handle_xdp_return --- pythonbpf/functions/functions_pass.py | 11 ++-------- pythonbpf/functions/return_utils.py | 30 ++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 6eff2e7..7d86257 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -9,7 +9,7 @@ from pythonbpf.binary_ops import handle_binary_op from pythonbpf.expr_pass import eval_expr, handle_expr -from .return_utils import _handle_none_return +from .return_utils import _handle_none_return, _handle_xdp_return logger = logging.getLogger(__name__) @@ -397,14 +397,7 @@ def handle_return(builder, stmt, local_sym_tab, ret_type): else: raise ValueError("Failed to evaluate return expression") elif isinstance(stmt.value, ast.Name): - if stmt.value.id == "XDP_PASS": - builder.ret(ir.Constant(ret_type, 2)) - return True - elif stmt.value.id == "XDP_DROP": - builder.ret(ir.Constant(ret_type, 1)) - return True - else: - raise ValueError("Failed to evaluate return expression") + return _handle_xdp_return(stmt, builder, ret_type) else: raise ValueError("Unsupported return value") diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py index 12a2d01..74df7d8 100644 --- a/pythonbpf/functions/return_utils.py +++ b/pythonbpf/functions/return_utils.py @@ -1,11 +1,39 @@ import logging -import ir +import ast + +from llvmlite import ir logger: logging.Logger = logging.getLogger(__name__) +XDP_ACTIONS = { + "XDP_ABORTED": 0, + "XDP_DROP": 1, + "XDP_PASS": 2, + "XDP_TX": 3, + "XDP_REDIRECT": 4, +} + def _handle_none_return(builder) -> bool: """Handle return or return None -> returns 0.""" builder.ret(ir.Constant(ir.IntType(64), 0)) logger.debug("Generated default return: 0") return True + + +def _handle_xdp_return(stmt: ast.Return, builder, ret_type) -> bool: + """Handle XDP returns""" + if not isinstance(stmt.value, ast.Name): + return False + + action_name = stmt.value.id + + if action_name not in XDP_ACTIONS: + raise ValueError( + f"Unknown XDP action: {action_name}. Available: {XDP_ACTIONS.keys()}" + ) + + value = XDP_ACTIONS[action_name] + builder.ret(ir.Constant(ret_type, value)) + logger.debug(f"Generated XDP action return: {action_name} = {value}") + return True From 192e03aa98908679e24526f77345aa7c8a343d01 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Sun, 5 Oct 2025 23:59:04 +0530 Subject: [PATCH 09/24] Add _handle_typed_constant_return --- pythonbpf/functions/return_utils.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py index 74df7d8..3c4e1c6 100644 --- a/pythonbpf/functions/return_utils.py +++ b/pythonbpf/functions/return_utils.py @@ -2,6 +2,7 @@ import ast from llvmlite import ir +from pythonbpf.type_deducer import ctypes_to_ir logger: logging.Logger = logging.getLogger(__name__) @@ -21,6 +22,23 @@ def _handle_none_return(builder) -> bool: return True +def _handle_typed_constant_return(call_type, return_value, builder, ret_type) -> bool: + """Handle typed constant return like: return c_int64(42)""" + + # call_type = stmt.value.func.id + expected_type = ctypes_to_ir(call_type) + + if expected_type != ret_type: + raise ValueError( + f"Return type mismatch: expected {ret_type}, got {expected_type}" + ) + + # return_value = stmt.value.args[0].value + builder.ret(ir.Constant(ret_type, return_value)) + logger.debug(f"Generated typed constant return: {call_type}({return_value})") + return True + + def _handle_xdp_return(stmt: ast.Return, builder, ret_type) -> bool: """Handle XDP returns""" if not isinstance(stmt.value, ast.Name): From c6fef1693ee024947b461df21247b082c646fc2b Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 00:03:34 +0530 Subject: [PATCH 10/24] Add _handle_binop_return --- pythonbpf/functions/return_utils.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py index 3c4e1c6..84a700c 100644 --- a/pythonbpf/functions/return_utils.py +++ b/pythonbpf/functions/return_utils.py @@ -3,6 +3,7 @@ from llvmlite import ir from pythonbpf.type_deducer import ctypes_to_ir +from pythonbpf.binary_ops import handle_binary_op logger: logging.Logger = logging.getLogger(__name__) @@ -39,6 +40,25 @@ def _handle_typed_constant_return(call_type, return_value, builder, ret_type) -> return True +def _handle_binop_return(arg, builder, ret_type, local_sym_tab) -> bool: + """Handle return with binary operation: return c_int64(x + 1)""" + + # result = handle_binary_op(stmt.value.args[0], builder, None, local_sym_tab) + result = handle_binary_op(arg, builder, None, local_sym_tab) + + if result is None: + raise ValueError("Failed to evaluate binary operation in return statement") + + val, val_type = result + + if val_type != ret_type: + raise ValueError(f"Return type mismatch: expected {ret_type}, got {val_type}") + + builder.ret(val) + logger.debug(f"Generated binary operation return: {val}") + return True + + def _handle_xdp_return(stmt: ast.Return, builder, ret_type) -> bool: """Handle XDP returns""" if not isinstance(stmt.value, ast.Name): From 23183da2e1d1649ea71d12f597f528110df59387 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 00:05:23 +0530 Subject: [PATCH 11/24] Add _handle_variable_return --- pythonbpf/functions/return_utils.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py index 84a700c..5637ff0 100644 --- a/pythonbpf/functions/return_utils.py +++ b/pythonbpf/functions/return_utils.py @@ -59,6 +59,25 @@ def _handle_binop_return(arg, builder, ret_type, local_sym_tab) -> bool: return True +def _handle_variable_return(var_name, builder, ret_type, local_sym_tab) -> bool: + """Handle return of a variable: return c_int64(my_var)""" + + # var_name = stmt.value.args[0].id + + if var_name not in local_sym_tab: + raise ValueError(f"Undefined variable in return: {var_name}") + + var = local_sym_tab[var_name].var + val = builder.load(var) + + if val.type != ret_type: + raise ValueError(f"Return type mismatch: expected {ret_type}, got {val.type}") + + builder.ret(val) + logger.debug(f"Generated variable return: {var_name}") + return True + + def _handle_xdp_return(stmt: ast.Return, builder, ret_type) -> bool: """Handle XDP returns""" if not isinstance(stmt.value, ast.Name): From f08bc9976c998c34c0d4a8ec5e8ed012a3c42065 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 02:22:43 +0530 Subject: [PATCH 12/24] Add _handle_wrapped_return --- pythonbpf/functions/return_utils.py | 41 ++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py index 5637ff0..a3f08af 100644 --- a/pythonbpf/functions/return_utils.py +++ b/pythonbpf/functions/return_utils.py @@ -7,6 +7,11 @@ logger: logging.Logger = logging.getLogger(__name__) +# TODO: Ideally there should be only 3 cases: +# - Return none +# - Return XDP +# - Return expr + XDP_ACTIONS = { "XDP_ABORTED": 0, "XDP_DROP": 1, @@ -26,7 +31,6 @@ def _handle_none_return(builder) -> bool: def _handle_typed_constant_return(call_type, return_value, builder, ret_type) -> bool: """Handle typed constant return like: return c_int64(42)""" - # call_type = stmt.value.func.id expected_type = ctypes_to_ir(call_type) if expected_type != ret_type: @@ -43,7 +47,6 @@ def _handle_typed_constant_return(call_type, return_value, builder, ret_type) -> def _handle_binop_return(arg, builder, ret_type, local_sym_tab) -> bool: """Handle return with binary operation: return c_int64(x + 1)""" - # result = handle_binary_op(stmt.value.args[0], builder, None, local_sym_tab) result = handle_binary_op(arg, builder, None, local_sym_tab) if result is None: @@ -62,8 +65,6 @@ def _handle_binop_return(arg, builder, ret_type, local_sym_tab) -> bool: def _handle_variable_return(var_name, builder, ret_type, local_sym_tab) -> bool: """Handle return of a variable: return c_int64(my_var)""" - # var_name = stmt.value.args[0].id - if var_name not in local_sym_tab: raise ValueError(f"Undefined variable in return: {var_name}") @@ -78,6 +79,38 @@ def _handle_variable_return(var_name, builder, ret_type, local_sym_tab) -> bool: return True +def _handle_wrapped_return(stmt: ast.Return, builder, ret_type, local_sym_tab) -> bool: + """Handle wrapped returns: return c_int64(42), return c_int64(x + 1), return c_int64(my_var)""" + + if not ( + isinstance(stmt.value, ast.Call) + and isinstance(stmt.value.func, ast.Name) + and len(stmt.value.args) == 1 + ): + return False + + arg = stmt.value.args[0] + + # Case 1: Constant value - return c_int64(42) + if isinstance(arg, ast.Constant) and isinstance(arg.value, int): + return _handle_typed_constant_return( + stmt.value.func.id, arg.value, builder, ret_type + ) + + # Case 2: Binary operation - return c_int64(x + 1) + elif isinstance(arg, ast.BinOp): + return _handle_binop_return(arg, builder, ret_type, local_sym_tab) + + # Case 3: Variable - return c_int64(my_var) + elif isinstance(arg, ast.Name): + if not arg.id: + raise ValueError("Variable return must have a type, e.g., c_int64") + return _handle_variable_return(arg.id, builder, ret_type, local_sym_tab) + + else: + raise ValueError(f"Unsupported return argument type: {type(arg).__name__}") + + def _handle_xdp_return(stmt: ast.Return, builder, ret_type) -> bool: """Handle XDP returns""" if not isinstance(stmt.value, ast.Name): From e4e92710c09623fbbf2cd6f177b9cc8845df8356 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 02:58:57 +0530 Subject: [PATCH 13/24] Move XDP pass above general return handling --- pythonbpf/functions/functions_pass.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 7d86257..63a6aed 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -354,8 +354,11 @@ def handle_if( def handle_return(builder, stmt, local_sym_tab, ret_type): + logger.info(f"Handling return statement: {ast.dump(stmt)}") if stmt.value is None: return _handle_none_return(builder) + elif isinstance(stmt.value, ast.Name): + return _handle_xdp_return(stmt, builder, ret_type) elif ( isinstance(stmt.value, ast.Call) and isinstance(stmt.value.func, ast.Name) @@ -396,8 +399,6 @@ def handle_return(builder, stmt, local_sym_tab, ret_type): return True else: raise ValueError("Failed to evaluate return expression") - elif isinstance(stmt.value, ast.Name): - return _handle_xdp_return(stmt, builder, ret_type) else: raise ValueError("Unsupported return value") From e6e2a6950654ae8353a4f956e80a90447322e328 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 03:02:08 +0530 Subject: [PATCH 14/24] Add _is_xdp_name --- pythonbpf/functions/functions_pass.py | 4 ++-- pythonbpf/functions/return_utils.py | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 63a6aed..16554f5 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -9,7 +9,7 @@ from pythonbpf.binary_ops import handle_binary_op from pythonbpf.expr_pass import eval_expr, handle_expr -from .return_utils import _handle_none_return, _handle_xdp_return +from .return_utils import _handle_none_return, _handle_xdp_return, _is_xdp_name logger = logging.getLogger(__name__) @@ -357,7 +357,7 @@ def handle_return(builder, stmt, local_sym_tab, ret_type): logger.info(f"Handling return statement: {ast.dump(stmt)}") if stmt.value is None: return _handle_none_return(builder) - elif isinstance(stmt.value, ast.Name): + elif isinstance(stmt.value, ast.Name) and _is_xdp_name(stmt.value.id): return _handle_xdp_return(stmt, builder, ret_type) elif ( isinstance(stmt.value, ast.Call) diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py index a3f08af..50f3a30 100644 --- a/pythonbpf/functions/return_utils.py +++ b/pythonbpf/functions/return_utils.py @@ -111,6 +111,11 @@ def _handle_wrapped_return(stmt: ast.Return, builder, ret_type, local_sym_tab) - raise ValueError(f"Unsupported return argument type: {type(arg).__name__}") +def _is_xdp_name(name: str) -> bool: + """Check if a name is an XDP action""" + return name in XDP_ACTIONS + + def _handle_xdp_return(stmt: ast.Return, builder, ret_type) -> bool: """Handle XDP returns""" if not isinstance(stmt.value, ast.Name): From 02885af1cae5e69b45783ff3f14dad18486824cc Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 03:36:44 +0530 Subject: [PATCH 15/24] Add binops to eval_expr --- pythonbpf/expr_pass.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pythonbpf/expr_pass.py b/pythonbpf/expr_pass.py index 40d0800..9ceb77f 100644 --- a/pythonbpf/expr_pass.py +++ b/pythonbpf/expr_pass.py @@ -153,6 +153,10 @@ def eval_expr( ) elif isinstance(expr, ast.Attribute): return _handle_attribute_expr(expr, local_sym_tab, structs_sym_tab, builder) + elif isinstance(expr, ast.BinOp): + from pythonbpf.binary_ops import handle_binary_op + + return handle_binary_op(expr, builder, None, local_sym_tab) logger.info("Unsupported expression evaluation") return None From f53ca3bd5b091910ca08f5832d7cf7c4e6ceb016 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 04:43:04 +0530 Subject: [PATCH 16/24] Add ctypes in eval_expr --- pythonbpf/expr_pass.py | 57 +++++++++++++++++++++++++++ pythonbpf/functions/functions_pass.py | 5 +++ pythonbpf/type_deducer.py | 34 +++++++++------- 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/pythonbpf/expr_pass.py b/pythonbpf/expr_pass.py index 9ceb77f..bcdb018 100644 --- a/pythonbpf/expr_pass.py +++ b/pythonbpf/expr_pass.py @@ -4,6 +4,8 @@ import logging from typing import Dict +from .type_deducer import ctypes_to_ir, is_ctypes + logger: Logger = logging.getLogger(__name__) @@ -88,6 +90,50 @@ def _handle_deref_call(expr: ast.Call, local_sym_tab: Dict, builder: ir.IRBuilde return val, local_sym_tab[arg.id].ir_type +def _handle_ctypes_call( + func, + module, + builder, + expr, + local_sym_tab, + map_sym_tab, + structs_sym_tab=None, +): + """Handle ctypes type constructor calls.""" + if len(expr.args) != 1: + logger.info("ctypes constructor takes exactly one argument") + return None + + arg = expr.args[0] + val = eval_expr( + func, + module, + builder, + arg, + local_sym_tab, + map_sym_tab, + structs_sym_tab, + ) + if val is None: + logger.info("Failed to evaluate argument to ctypes constructor") + return None + call_type = expr.func.id + expected_type = ctypes_to_ir(call_type) + if expected_type is None: + logger.info(f"Unsupported ctypes type: {call_type}") + return None + if val[1] != expected_type: + # NOTE: We are only considering casting to and from int types for now + if isinstance(val[1], ir.IntType) and isinstance(expected_type, ir.IntType): + if val[1].width < expected_type.width: + val = (builder.sext(val[0], expected_type), expected_type) + else: + val = (builder.trunc(val[0], expected_type), expected_type) + else: + raise ValueError(f"Type mismatch: expected {expected_type}, got {val[1]}") + return val + + def eval_expr( func, module, @@ -106,6 +152,17 @@ def eval_expr( if isinstance(expr.func, ast.Name) and expr.func.id == "deref": return _handle_deref_call(expr, local_sym_tab, builder) + if isinstance(expr.func, ast.Name) and is_ctypes(expr.func.id): + return _handle_ctypes_call( + func, + module, + builder, + expr, + local_sym_tab, + map_sym_tab, + structs_sym_tab, + ) + # delayed import to avoid circular dependency from pythonbpf.helper import HelperHandlerRegistry, handle_helper_call diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 16554f5..8d4a559 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -359,6 +359,11 @@ def handle_return(builder, stmt, local_sym_tab, ret_type): return _handle_none_return(builder) elif isinstance(stmt.value, ast.Name) and _is_xdp_name(stmt.value.id): return _handle_xdp_return(stmt, builder, ret_type) + elif True: + val = eval_expr(None, None, builder, stmt.value, local_sym_tab, {}, {}) + logger.info(f"Evaluated return expression to {val}") + builder.ret(val[0]) + return True elif ( isinstance(stmt.value, ast.Call) and isinstance(stmt.value.func, ast.Name) diff --git a/pythonbpf/type_deducer.py b/pythonbpf/type_deducer.py index 909d33c..9867cc6 100644 --- a/pythonbpf/type_deducer.py +++ b/pythonbpf/type_deducer.py @@ -1,24 +1,28 @@ from llvmlite import ir # TODO: THIS IS NOT SUPPOSED TO MATCH STRINGS :skull: +mapping = { + "c_int8": ir.IntType(8), + "c_uint8": ir.IntType(8), + "c_int16": ir.IntType(16), + "c_uint16": ir.IntType(16), + "c_int32": ir.IntType(32), + "c_uint32": ir.IntType(32), + "c_int64": ir.IntType(64), + "c_uint64": ir.IntType(64), + "c_float": ir.FloatType(), + "c_double": ir.DoubleType(), + "c_void_p": ir.IntType(64), + # Not so sure about this one + "str": ir.PointerType(ir.IntType(8)), +} def ctypes_to_ir(ctype: str): - mapping = { - "c_int8": ir.IntType(8), - "c_uint8": ir.IntType(8), - "c_int16": ir.IntType(16), - "c_uint16": ir.IntType(16), - "c_int32": ir.IntType(32), - "c_uint32": ir.IntType(32), - "c_int64": ir.IntType(64), - "c_uint64": ir.IntType(64), - "c_float": ir.FloatType(), - "c_double": ir.DoubleType(), - "c_void_p": ir.IntType(64), - # Not so sure about this one - "str": ir.PointerType(ir.IntType(8)), - } if ctype in mapping: return mapping[ctype] raise NotImplementedError(f"No mapping for {ctype}") + + +def is_ctypes(ctype: str) -> bool: + return ctype in mapping From b75dc82f90e9c7438da20ce044da5f0af14cc36c Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 04:44:55 +0530 Subject: [PATCH 17/24] Remove clutter from handle_return --- pythonbpf/functions/functions_pass.py | 44 +-------------------------- 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 8d4a559..76f87bb 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -359,53 +359,11 @@ def handle_return(builder, stmt, local_sym_tab, ret_type): return _handle_none_return(builder) elif isinstance(stmt.value, ast.Name) and _is_xdp_name(stmt.value.id): return _handle_xdp_return(stmt, builder, ret_type) - elif True: + else: val = eval_expr(None, None, builder, stmt.value, local_sym_tab, {}, {}) logger.info(f"Evaluated return expression to {val}") builder.ret(val[0]) return True - elif ( - isinstance(stmt.value, ast.Call) - and isinstance(stmt.value.func, ast.Name) - and len(stmt.value.args) == 1 - ): - if isinstance(stmt.value.args[0], ast.Constant) and isinstance( - stmt.value.args[0].value, int - ): - call_type = stmt.value.func.id - if ctypes_to_ir(call_type) != ret_type: - raise ValueError( - "Return type mismatch: expected" - f"{ctypes_to_ir(call_type)}, got {call_type}" - ) - else: - builder.ret(ir.Constant(ret_type, stmt.value.args[0].value)) - return True - elif isinstance(stmt.value.args[0], ast.BinOp): - # TODO: Should be routed through eval_expr - val = handle_binary_op(stmt.value.args[0], builder, None, local_sym_tab) - if val is None: - raise ValueError("Failed to evaluate return expression") - if val[1] != ret_type: - raise ValueError( - f"Return type mismatch: expected {ret_type}, got {val[1]}" - ) - builder.ret(val[0]) - return True - elif isinstance(stmt.value.args[0], ast.Name): - if stmt.value.args[0].id in local_sym_tab: - var = local_sym_tab[stmt.value.args[0].id].var - val = builder.load(var) - if val.type != ret_type: - raise ValueError( - f"Return type mismatch: expected {ret_type}, got {val.type}" - ) - builder.ret(val) - return True - else: - raise ValueError("Failed to evaluate return expression") - else: - raise ValueError("Unsupported return value") def process_stmt( From 3e68d6df4f757dedd5269112a0024a6ae92c8ec5 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 04:57:04 +0530 Subject: [PATCH 18/24] Add passing test examples for return statements --- tests/passing_tests/return/binop_const.py | 18 +++++++++++++++++ tests/passing_tests/return/binop_var.py | 19 ++++++++++++++++++ tests/passing_tests/return/int.py | 18 +++++++++++++++++ tests/passing_tests/return/null.py | 18 +++++++++++++++++ tests/passing_tests/return/typecast_binops.py | 20 +++++++++++++++++++ tests/passing_tests/return/typecast_const.py | 18 +++++++++++++++++ tests/passing_tests/return/typecast_var.py | 19 ++++++++++++++++++ tests/passing_tests/return/var.py | 19 ++++++++++++++++++ 8 files changed, 149 insertions(+) create mode 100644 tests/passing_tests/return/binop_const.py create mode 100644 tests/passing_tests/return/binop_var.py create mode 100644 tests/passing_tests/return/int.py create mode 100644 tests/passing_tests/return/null.py create mode 100644 tests/passing_tests/return/typecast_binops.py create mode 100644 tests/passing_tests/return/typecast_const.py create mode 100644 tests/passing_tests/return/typecast_var.py create mode 100644 tests/passing_tests/return/var.py diff --git a/tests/passing_tests/return/binop_const.py b/tests/passing_tests/return/binop_const.py new file mode 100644 index 0000000..faafd1f --- /dev/null +++ b/tests/passing_tests/return/binop_const.py @@ -0,0 +1,18 @@ +from pythonbpf import bpf, section, bpfglobal, compile +from ctypes import c_void_p, c_int64 + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello_world(ctx: c_void_p) -> c_int64: + print("Hello, World!") + return 1 + 1 - 2 + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() diff --git a/tests/passing_tests/return/binop_var.py b/tests/passing_tests/return/binop_var.py new file mode 100644 index 0000000..32b5784 --- /dev/null +++ b/tests/passing_tests/return/binop_var.py @@ -0,0 +1,19 @@ +from pythonbpf import bpf, section, bpfglobal, compile +from ctypes import c_void_p, c_int64 + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello_world(ctx: c_void_p) -> c_int64: + print("Hello, World!") + a = 2 + return a - 2 + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() diff --git a/tests/passing_tests/return/int.py b/tests/passing_tests/return/int.py new file mode 100644 index 0000000..b20b4a0 --- /dev/null +++ b/tests/passing_tests/return/int.py @@ -0,0 +1,18 @@ +from pythonbpf import bpf, section, bpfglobal, compile +from ctypes import c_void_p, c_int64 + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello_world(ctx: c_void_p) -> c_int64: + print("Hello, World!") + return 1 + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() diff --git a/tests/passing_tests/return/null.py b/tests/passing_tests/return/null.py new file mode 100644 index 0000000..34a1492 --- /dev/null +++ b/tests/passing_tests/return/null.py @@ -0,0 +1,18 @@ +from pythonbpf import bpf, section, bpfglobal, compile +from ctypes import c_void_p, c_int64 + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello_world(ctx: c_void_p) -> c_int64: + print("Hello, World!") + return + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() diff --git a/tests/passing_tests/return/typecast_binops.py b/tests/passing_tests/return/typecast_binops.py new file mode 100644 index 0000000..c58ba41 --- /dev/null +++ b/tests/passing_tests/return/typecast_binops.py @@ -0,0 +1,20 @@ +from pythonbpf import bpf, section, bpfglobal, compile +from ctypes import c_void_p, c_int32 + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello_world(ctx: c_void_p) -> c_int32: + print("Hello, World!") + a = 1 # int64 + x = 1 # int64 + return c_int32(a - x) # typecast to int32 + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() diff --git a/tests/passing_tests/return/typecast_const.py b/tests/passing_tests/return/typecast_const.py new file mode 100644 index 0000000..50cc26f --- /dev/null +++ b/tests/passing_tests/return/typecast_const.py @@ -0,0 +1,18 @@ +from pythonbpf import bpf, section, bpfglobal, compile +from ctypes import c_void_p, c_int32 + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello_world(ctx: c_void_p) -> c_int32: + print("Hello, World!") + return c_int32(1) + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() diff --git a/tests/passing_tests/return/typecast_var.py b/tests/passing_tests/return/typecast_var.py new file mode 100644 index 0000000..1960edd --- /dev/null +++ b/tests/passing_tests/return/typecast_var.py @@ -0,0 +1,19 @@ +from pythonbpf import bpf, section, bpfglobal, compile +from ctypes import c_void_p, c_int32 + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello_world(ctx: c_void_p) -> c_int32: + print("Hello, World!") + a = 1 # int64 + return c_int32(a) # typecast to int32 + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() diff --git a/tests/passing_tests/return/var.py b/tests/passing_tests/return/var.py new file mode 100644 index 0000000..26fb34d --- /dev/null +++ b/tests/passing_tests/return/var.py @@ -0,0 +1,19 @@ +from pythonbpf import bpf, section, bpfglobal, compile +from ctypes import c_void_p, c_int64 + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello_world(ctx: c_void_p) -> c_int64: + print("Hello, World!") + a = 1 + return a + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() From 7b0e8a2fca3787214d1b3a73aa8cea47daa92acc Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 04:59:20 +0530 Subject: [PATCH 19/24] Add xdp example for passing return type --- tests/passing_tests/return/xdp.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/passing_tests/return/xdp.py diff --git a/tests/passing_tests/return/xdp.py b/tests/passing_tests/return/xdp.py new file mode 100644 index 0000000..3c0f5d8 --- /dev/null +++ b/tests/passing_tests/return/xdp.py @@ -0,0 +1,19 @@ +from pythonbpf import bpf, section, bpfglobal, compile +from ctypes import c_void_p, c_int64 +from pythonbpf.helper import XDP_PASS + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello_world(ctx: c_void_p) -> c_int64: + print("Hello, World!") + return XDP_PASS + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile() From 9aff614ff5f9dbfe7fce5b3e73586811db034305 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 05:02:02 +0530 Subject: [PATCH 20/24] Remove unnecessary parts from return_utils --- pythonbpf/functions/return_utils.py | 85 ----------------------------- 1 file changed, 85 deletions(-) diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py index 50f3a30..7d11f87 100644 --- a/pythonbpf/functions/return_utils.py +++ b/pythonbpf/functions/return_utils.py @@ -2,8 +2,6 @@ import ast from llvmlite import ir -from pythonbpf.type_deducer import ctypes_to_ir -from pythonbpf.binary_ops import handle_binary_op logger: logging.Logger = logging.getLogger(__name__) @@ -28,89 +26,6 @@ def _handle_none_return(builder) -> bool: return True -def _handle_typed_constant_return(call_type, return_value, builder, ret_type) -> bool: - """Handle typed constant return like: return c_int64(42)""" - - expected_type = ctypes_to_ir(call_type) - - if expected_type != ret_type: - raise ValueError( - f"Return type mismatch: expected {ret_type}, got {expected_type}" - ) - - # return_value = stmt.value.args[0].value - builder.ret(ir.Constant(ret_type, return_value)) - logger.debug(f"Generated typed constant return: {call_type}({return_value})") - return True - - -def _handle_binop_return(arg, builder, ret_type, local_sym_tab) -> bool: - """Handle return with binary operation: return c_int64(x + 1)""" - - result = handle_binary_op(arg, builder, None, local_sym_tab) - - if result is None: - raise ValueError("Failed to evaluate binary operation in return statement") - - val, val_type = result - - if val_type != ret_type: - raise ValueError(f"Return type mismatch: expected {ret_type}, got {val_type}") - - builder.ret(val) - logger.debug(f"Generated binary operation return: {val}") - return True - - -def _handle_variable_return(var_name, builder, ret_type, local_sym_tab) -> bool: - """Handle return of a variable: return c_int64(my_var)""" - - if var_name not in local_sym_tab: - raise ValueError(f"Undefined variable in return: {var_name}") - - var = local_sym_tab[var_name].var - val = builder.load(var) - - if val.type != ret_type: - raise ValueError(f"Return type mismatch: expected {ret_type}, got {val.type}") - - builder.ret(val) - logger.debug(f"Generated variable return: {var_name}") - return True - - -def _handle_wrapped_return(stmt: ast.Return, builder, ret_type, local_sym_tab) -> bool: - """Handle wrapped returns: return c_int64(42), return c_int64(x + 1), return c_int64(my_var)""" - - if not ( - isinstance(stmt.value, ast.Call) - and isinstance(stmt.value.func, ast.Name) - and len(stmt.value.args) == 1 - ): - return False - - arg = stmt.value.args[0] - - # Case 1: Constant value - return c_int64(42) - if isinstance(arg, ast.Constant) and isinstance(arg.value, int): - return _handle_typed_constant_return( - stmt.value.func.id, arg.value, builder, ret_type - ) - - # Case 2: Binary operation - return c_int64(x + 1) - elif isinstance(arg, ast.BinOp): - return _handle_binop_return(arg, builder, ret_type, local_sym_tab) - - # Case 3: Variable - return c_int64(my_var) - elif isinstance(arg, ast.Name): - if not arg.id: - raise ValueError("Variable return must have a type, e.g., c_int64") - return _handle_variable_return(arg.id, builder, ret_type, local_sym_tab) - - else: - raise ValueError(f"Unsupported return argument type: {type(arg).__name__}") - - def _is_xdp_name(name: str) -> bool: """Check if a name is an XDP action""" return name in XDP_ACTIONS From abef68c2744691415dd8dd8024b38596d59cb344 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 05:04:06 +0530 Subject: [PATCH 21/24] Remove redundant TODO from return_utils --- pythonbpf/functions/return_utils.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py index 7d11f87..c24e277 100644 --- a/pythonbpf/functions/return_utils.py +++ b/pythonbpf/functions/return_utils.py @@ -5,11 +5,6 @@ logger: logging.Logger = logging.getLogger(__name__) -# TODO: Ideally there should be only 3 cases: -# - Return none -# - Return XDP -# - Return expr - XDP_ACTIONS = { "XDP_ABORTED": 0, "XDP_DROP": 1, From 2f0dd20f1ec73445d98083cab7388213df71ed2d Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 05:09:03 +0530 Subject: [PATCH 22/24] Add false case for _handle_xdp in return_utils --- pythonbpf/functions/return_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pythonbpf/functions/return_utils.py b/pythonbpf/functions/return_utils.py index c24e277..c69e416 100644 --- a/pythonbpf/functions/return_utils.py +++ b/pythonbpf/functions/return_utils.py @@ -37,6 +37,7 @@ def _handle_xdp_return(stmt: ast.Return, builder, ret_type) -> bool: raise ValueError( f"Unknown XDP action: {action_name}. Available: {XDP_ACTIONS.keys()}" ) + return False value = XDP_ACTIONS[action_name] builder.ret(ir.Constant(ret_type, value)) From 0bfb3855b6a282c1fe28529f405eb0102628bb2d Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 05:10:22 +0530 Subject: [PATCH 23/24] Remove dead code from _handle_ctypes_call --- pythonbpf/expr_pass.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pythonbpf/expr_pass.py b/pythonbpf/expr_pass.py index bcdb018..56d047e 100644 --- a/pythonbpf/expr_pass.py +++ b/pythonbpf/expr_pass.py @@ -119,9 +119,7 @@ def _handle_ctypes_call( return None call_type = expr.func.id expected_type = ctypes_to_ir(call_type) - if expected_type is None: - logger.info(f"Unsupported ctypes type: {call_type}") - return None + if val[1] != expected_type: # NOTE: We are only considering casting to and from int types for now if isinstance(val[1], ir.IntType) and isinstance(expected_type, ir.IntType): From 5066cd4cfee43ddc8bfed7ffb7b8d35ca6d44460 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 6 Oct 2025 05:11:33 +0530 Subject: [PATCH 24/24] Use named args for eval_expr call in handle_return --- pythonbpf/functions/functions_pass.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 76f87bb..18904ec 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -360,7 +360,15 @@ def handle_return(builder, stmt, local_sym_tab, ret_type): elif isinstance(stmt.value, ast.Name) and _is_xdp_name(stmt.value.id): return _handle_xdp_return(stmt, builder, ret_type) else: - val = eval_expr(None, None, builder, stmt.value, local_sym_tab, {}, {}) + val = eval_expr( + func=None, + module=None, + builder=builder, + expr=stmt.value, + local_sym_tab=local_sym_tab, + map_sym_tab={}, + structs_sym_tab={}, + ) logger.info(f"Evaluated return expression to {val}") builder.ret(val[0]) return True