Skip to content

Commit f46c1d6

Browse files
committed
Fix variable naming consistency in eBPF C code generation.
1 parent f1b0977 commit f46c1d6

2 files changed

Lines changed: 133 additions & 11 deletions

File tree

src/ebpf_c_codegen.ml

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,17 +220,18 @@ let mark_register_inlinable ctx reg expr =
220220
(** Optimization: Generate meaningful variable names *)
221221
let get_meaningful_var_name ctx reg ir_type =
222222
if ctx.enable_temp_var_optimization then
223-
(* For return-like types, always use consistent naming *)
224-
let should_use_val_prefix = match ir_type with
225-
| IRU32 | IRI32 | IRU64 | IRI64 -> true
226-
| _ -> false
227-
in
228-
if should_use_val_prefix then
229-
sprintf "val_%d" reg
230-
else
231-
match Hashtbl.find_opt ctx.register_name_hints reg with
232-
| Some hint -> sprintf "%s_%d" hint reg
233-
| None ->
223+
(* First check if there are explicit register hints - these take precedence *)
224+
match Hashtbl.find_opt ctx.register_name_hints reg with
225+
| Some hint -> sprintf "%s_%d" hint reg
226+
| None ->
227+
(* For return-like types, use consistent naming when no explicit hints exist *)
228+
let should_use_val_prefix = match ir_type with
229+
| IRU32 | IRI32 | IRU64 | IRI64 -> true
230+
| _ -> false
231+
in
232+
if should_use_val_prefix then
233+
sprintf "val_%d" reg
234+
else
234235
let type_hint = match ir_type with
235236
| IRBool -> "cond"
236237
| IRStr _ -> "str"

tests/test_break_continue.ml

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
open Alcotest
1818
open Kernelscript.Parse
1919
open Kernelscript.Type_checker
20+
open Kernelscript.Ir
21+
open Kernelscript.Ebpf_c_codegen
22+
open Kernelscript.Ast
23+
24+
(** Common test position for IR/codegen tests *)
25+
let test_pos = make_position 1 1 "test.ks"
2026

2127
(** Helper function to parse and evaluate a program with break/continue *)
2228
let parse_and_check_break_continue program_text =
@@ -196,6 +202,118 @@ let test_break_evaluation () =
196202
with
197203
| e -> fail ("Failed break evaluation test: " ^ Printexc.to_string e)
198204

205+
(** Test that verifies the fix for break/continue in unbound loops
206+
This test ensures that callback functions have consistent variable naming
207+
between declarations and usage. Previously, variables were declared as
208+
tmp_X but referenced as val_X, causing compilation errors.
209+
*)
210+
let test_break_continue_unbound_variable_naming () =
211+
let ctx = create_c_context () in
212+
213+
(* Create registers for variables that would be used in break/continue logic *)
214+
let counter_reg = 1 in
215+
let condition_reg = 2 in
216+
let temp_reg = 3 in
217+
218+
(* Create IR values representing loop variables *)
219+
let counter_val = make_ir_value (IRRegister counter_reg) IRU32 test_pos in
220+
let condition_val = make_ir_value (IRRegister condition_reg) IRBool test_pos in
221+
let temp_val = make_ir_value (IRRegister temp_reg) IRU32 test_pos in
222+
223+
(* Create a modulo operation and comparison (similar to the original failing case) *)
224+
let two_val = make_ir_value (IRLiteral (IntLit (2, None))) IRU32 test_pos in
225+
let zero_val = make_ir_value (IRLiteral (IntLit (0, None))) IRU32 test_pos in
226+
227+
(* Create IR instructions that would be in a bpf_loop callback *)
228+
let mod_expr = make_ir_expr (IRBinOp (counter_val, IRMod, two_val)) IRU32 test_pos in
229+
let mod_assign = make_ir_instruction (IRAssign (temp_val, mod_expr)) test_pos in
230+
231+
let eq_expr = make_ir_expr (IRBinOp (temp_val, IREq, zero_val)) IRBool test_pos in
232+
let eq_assign = make_ir_instruction (IRAssign (condition_val, eq_expr)) test_pos in
233+
234+
(* Create the bpf_loop instruction with these body instructions *)
235+
let start_val = make_ir_value (IRLiteral (IntLit (0, None))) IRU32 test_pos in
236+
let end_val = make_ir_value (IRLiteral (IntLit (1000, None))) IRU32 test_pos in
237+
let ctx_val = make_ir_value (IRRegister 10) (IRPointer (IRU8, make_bounds_info ())) test_pos in
238+
let body_instructions = [mod_assign; eq_assign] in
239+
240+
let bpf_loop_instr = make_ir_instruction
241+
(IRBpfLoop (start_val, end_val, counter_val, ctx_val, body_instructions))
242+
test_pos in
243+
244+
(* Generate C code for the bpf_loop instruction *)
245+
generate_c_instruction ctx bpf_loop_instr;
246+
247+
(* Get the generated code *)
248+
let _output = String.concat "\n" ctx.output_lines in
249+
250+
(* Extract any pending callbacks (this is where the fix matters) *)
251+
let callback_code = String.concat "\n" ctx.pending_callbacks in
252+
253+
(* Verify that the callback code doesn't contain inconsistent variable naming *)
254+
let has_consistent_naming =
255+
(* Check that if variables are declared as tmp_X, they're also used as tmp_X *)
256+
let tmp_declarations = Str.split (Str.regexp "tmp_[0-9]+") callback_code in
257+
let val_usage = Str.split (Str.regexp "val_[0-9]+") callback_code in
258+
(* If there are tmp declarations, there shouldn't be val usage in the same callback *)
259+
if List.length tmp_declarations > 1 then
260+
List.length val_usage <= 1 (* Only the split creates one extra element *)
261+
else
262+
true
263+
in
264+
265+
check bool "Variable naming is consistent in callback" true has_consistent_naming;
266+
267+
(* Also verify that the generated code contains a callback function *)
268+
let has_callback = String.length callback_code > 0 in
269+
check bool "Callback function was generated" true has_callback;
270+
271+
(* Additional check: verify no undeclared variable usage *)
272+
let lines = String.split_on_char '\n' callback_code in
273+
let has_undeclared_usage = List.exists (fun line ->
274+
(* Look for patterns like "val_X = " where val_X wasn't declared *)
275+
Str.string_match (Str.regexp ".*val_[0-9]+ =.*") line 0 &&
276+
not (Str.string_match (Str.regexp ".*__u32 val_[0-9]+.*") line 0)
277+
) lines in
278+
279+
check bool "No undeclared variable usage" false has_undeclared_usage
280+
281+
let test_register_hints_respected_in_callbacks () =
282+
let ctx = create_c_context () in
283+
284+
(* Set register hints to use "tmp" prefix *)
285+
Hashtbl.replace ctx.register_name_hints 1 "tmp";
286+
Hashtbl.replace ctx.register_name_hints 2 "tmp";
287+
288+
(* Create variables with types that would normally use "val" prefix *)
289+
let _val1 = make_ir_value (IRRegister 1) IRU32 test_pos in
290+
let _val2 = make_ir_value (IRRegister 2) IRU32 test_pos in
291+
292+
(* Generate variable names *)
293+
let name1 = get_meaningful_var_name ctx 1 IRU32 in
294+
let name2 = get_meaningful_var_name ctx 2 IRU32 in
295+
296+
(* With the fix, these should respect the hints and use "tmp" not "val" *)
297+
check string "Register 1 uses hint prefix" "tmp_1" name1;
298+
check string "Register 2 uses hint prefix" "tmp_2" name2
299+
300+
let test_no_hints_uses_type_based_naming () =
301+
let ctx = create_c_context () in
302+
303+
(* Don't set any register hints *)
304+
305+
(* Create variables with types that should use "val" prefix when no hints *)
306+
let _val1 = make_ir_value (IRRegister 1) IRU32 test_pos in
307+
let _val2 = make_ir_value (IRRegister 2) IRBool test_pos in
308+
309+
(* Generate variable names *)
310+
let name1 = get_meaningful_var_name ctx 1 IRU32 in
311+
let name2 = get_meaningful_var_name ctx 2 IRBool in
312+
313+
(* Without hints, should use type-based naming *)
314+
check string "U32 without hints uses val prefix" "val_1" name1;
315+
check string "Bool without hints uses cond prefix" "cond_2" name2
316+
199317
let break_continue_tests = [
200318
"break_statement_parsing", `Quick, test_break_statement_parsing;
201319
"continue_statement_parsing", `Quick, test_continue_statement_parsing;
@@ -206,6 +324,9 @@ let break_continue_tests = [
206324
"break_continue_in_nested_conditional", `Quick, test_break_continue_in_nested_conditional;
207325
"multiple_break_continue_statements", `Quick, test_multiple_break_continue_statements;
208326
"break_evaluation", `Quick, test_break_evaluation;
327+
"break_continue_unbound_variable_naming", `Quick, test_break_continue_unbound_variable_naming;
328+
"register_hints_respected_in_callbacks", `Quick, test_register_hints_respected_in_callbacks;
329+
"no_hints_uses_type_based_naming", `Quick, test_no_hints_uses_type_based_naming;
209330
]
210331

211332
let () =

0 commit comments

Comments
 (0)