Skip to content

Commit 62b1cc7

Browse files
committed
feat: add map[key].field op= rhs compound assignment
Signed-off-by: Cong Wang <cwang@multikernel.io>
1 parent e4f979c commit 62b1cc7

12 files changed

Lines changed: 182 additions & 3 deletions

src/ast.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ and stmt_desc =
285285
| Assignment of string * expr
286286
| CompoundAssignment of string * binary_op * expr (* var op= expr *)
287287
| CompoundIndexAssignment of expr * expr * binary_op * expr (* map[key] op= expr *)
288+
| CompoundFieldIndexAssignment of expr * expr * string * binary_op * expr
289+
(* map[key].field op= expr *)
288290
| FieldAssignment of expr * string * expr (* object.field = value *)
289291
| ArrowAssignment of expr * string * expr (* pointer->field = value *)
290292
| IndexAssignment of expr * expr * expr (* map[key] = value *)
@@ -818,6 +820,10 @@ and string_of_stmt stmt =
818820
Printf.sprintf "%s %s= %s;" name (string_of_binary_op op) (string_of_expr expr)
819821
| CompoundIndexAssignment (map_expr, key_expr, op, value_expr) ->
820822
Printf.sprintf "%s[%s] %s= %s;" (string_of_expr map_expr) (string_of_expr key_expr) (string_of_binary_op op) (string_of_expr value_expr)
823+
| CompoundFieldIndexAssignment (map_expr, key_expr, field, op, value_expr) ->
824+
Printf.sprintf "%s[%s].%s %s= %s;"
825+
(string_of_expr map_expr) (string_of_expr key_expr) field
826+
(string_of_binary_op op) (string_of_expr value_expr)
821827
| FieldAssignment (obj_expr, field, value_expr) ->
822828
Printf.sprintf "%s.%s = %s;" (string_of_expr obj_expr) field (string_of_expr value_expr)
823829
| ArrowAssignment (obj_expr, field, value_expr) ->

src/ebpf_c_codegen.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,6 +1952,10 @@ and generate_c_instruction ctx ir_instr =
19521952
(* Other string expressions (concatenation, etc.) *)
19531953
let init_str = generate_c_expression ctx init_expr in
19541954
emit_line ctx (sprintf "%s %s = %s;" type_str var_name init_str)
1955+
| IRPointer _, IRValue src_val when (match src_val.value_desc with IRMapAccess _ -> true | _ -> false) ->
1956+
(* Pointer-typed variable initialized from a map lookup: keep the pointer. *)
1957+
let init_str = generate_c_value ~auto_deref_map_access:false ctx src_val in
1958+
emit_line ctx (sprintf "%s %s = %s;" type_str var_name init_str)
19551959
| _ ->
19561960
(* Regular non-string assignment *)
19571961
let init_str = generate_c_expression ctx init_expr in

src/evaluator.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,11 @@ and eval_statement ctx stmt =
804804
in
805805
let result = eval_binary_op current_val op value_val stmt.stmt_pos in
806806
Hashtbl.replace map_store key_str result
807+
808+
| CompoundFieldIndexAssignment (_, _, _, _, _) ->
809+
(* The interpreter is used for compile-time evaluation only;
810+
struct-field compound assignment on map values is a runtime construct. *)
811+
eval_error "map[key].field op= rhs is not supported in the interpreter" stmt.stmt_pos
807812

808813
| FieldAssignment (obj_expr, _field, value_expr) ->
809814
(* For evaluation purposes, treat config field assignment as no-op *)

src/ir_generator.ml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,6 +2044,49 @@ and lower_statement ctx stmt =
20442044
stmt.stmt_pos in
20452045
emit_instruction ctx if_instr
20462046

2047+
| Ast.CompoundFieldIndexAssignment (map_expr, key_expr, field, op, value_expr) ->
2048+
(* Desugar `map[k].field op= rhs` to:
2049+
if (var __cidx_field_N = map[k]) {
2050+
__cidx_field_N.field = __cidx_field_N.field op rhs
2051+
}
2052+
The IfLet lowering (above) handles the presence check; the field-store
2053+
lowers to a pointer-checked `ptr->field = ...` via IRStructFieldAssignment.
2054+
We look up the field's AST type from the map's value-struct definition so
2055+
that the synthesized FieldAccess/BinaryOp get correct expr_type — without
2056+
this the IR generator defaults to IRU32, mis-sizing wider fields. *)
2057+
let pos = stmt.stmt_pos in
2058+
let synth_name = generate_temp_variable ctx "cidx_field" in
2059+
let map_name = match map_expr.expr_desc with
2060+
| Ast.Identifier mn -> mn
2061+
| _ -> failwith "Compound field-index assignment requires a map identifier"
2062+
in
2063+
let map_def = Hashtbl.find ctx.maps map_name in
2064+
let field_ast_type =
2065+
let rec resolve_struct_name = function
2066+
| Ast.Struct n | Ast.UserType n -> Some n
2067+
| _ -> None
2068+
and resolve t =
2069+
match resolve_struct_name t with
2070+
| Some n ->
2071+
(match Symbol_table.lookup_symbol ctx.symbol_table n with
2072+
| Some { kind = Symbol_table.TypeDef (Ast.StructDef (_, fs, _)); _ } ->
2073+
(try Some (List.assoc field fs) with Not_found -> None)
2074+
| _ -> None)
2075+
| None -> None
2076+
in
2077+
resolve map_def.ast_value_type
2078+
in
2079+
let mk_expr ?ty d =
2080+
{ Ast.expr_desc = d; expr_pos = pos; expr_type = ty;
2081+
type_checked = false; program_context = None; map_scope = None } in
2082+
let access = mk_expr (Ast.ArrayAccess (map_expr, key_expr)) in
2083+
let tmp_id = mk_expr (Ast.Identifier synth_name) in
2084+
let cur_field = mk_expr ?ty:field_ast_type (Ast.FieldAccess (tmp_id, field)) in
2085+
let bin = mk_expr ?ty:field_ast_type (Ast.BinaryOp (cur_field, op, value_expr)) in
2086+
let store = { Ast.stmt_desc = Ast.FieldAssignment (tmp_id, field, bin); stmt_pos = pos } in
2087+
let iflet = { Ast.stmt_desc = Ast.IfLet (synth_name, access, [store], None); stmt_pos = pos } in
2088+
lower_statement ctx iflet
2089+
20472090
| Ast.IfLet (name, expr, then_stmts, else_opt) ->
20482091
(* Desugar `if (var name = expr) { T } else { E }` into:
20492092
var name = expr

src/multi_program_analyzer.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ let analyze_map_usage (programs: program_def list) (global_maps: map_declaration
187187
analyze_expr_for_maps prog_name map_expr;
188188
analyze_expr_for_maps prog_name key_expr;
189189
analyze_expr_for_maps prog_name value_expr
190+
| CompoundFieldIndexAssignment (map_expr, key_expr, _, _, value_expr) ->
191+
analyze_expr_for_maps prog_name map_expr;
192+
analyze_expr_for_maps prog_name key_expr;
193+
analyze_expr_for_maps prog_name value_expr
190194
| FieldAssignment (obj_expr, _, value_expr) ->
191195
analyze_expr_for_maps prog_name obj_expr;
192196
analyze_expr_for_maps prog_name value_expr

src/parse.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ let validate_ast ast =
9696
| CompoundAssignment (_, _, expr) -> validate_expr expr
9797
| CompoundIndexAssignment (map_expr, key_expr, _, value_expr) ->
9898
validate_expr map_expr && validate_expr key_expr && validate_expr value_expr
99+
| CompoundFieldIndexAssignment (map_expr, key_expr, _, _, value_expr) ->
100+
validate_expr map_expr && validate_expr key_expr && validate_expr value_expr
99101
| FieldAssignment (obj_expr, _, value_expr) ->
100102
validate_expr obj_expr && validate_expr value_expr
101103
| ArrowAssignment (obj_expr, _, value_expr) ->

src/parser.mly

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
%type <Ast.statement> assignment_or_expression_statement
127127
%type <Ast.statement> compound_assignment_statement
128128
%type <Ast.statement> compound_index_assignment_statement
129+
%type <Ast.statement> compound_field_index_assignment_statement
129130
%type <Ast.statement> field_assignment_statement
130131
%type <Ast.statement> arrow_assignment_statement
131132
%type <Ast.statement> index_assignment_statement
@@ -313,6 +314,7 @@ statement:
313314
| index_assignment_statement { $1 }
314315
| compound_assignment_statement { $1 }
315316
| compound_index_assignment_statement { $1 }
317+
| compound_field_index_assignment_statement { $1 }
316318
| assignment_or_expression_statement { $1 }
317319
| return_statement { $1 }
318320
| if_statement { $1 }
@@ -380,6 +382,18 @@ compound_index_assignment_statement:
380382
| expression LBRACKET expression RBRACKET MODULO_ASSIGN expression
381383
{ make_stmt (CompoundIndexAssignment ($1, $3, Mod, $6)) (make_pos ()) }
382384

385+
compound_field_index_assignment_statement:
386+
| expression LBRACKET expression RBRACKET DOT IDENTIFIER PLUS_ASSIGN expression
387+
{ make_stmt (CompoundFieldIndexAssignment ($1, $3, $6, Add, $8)) (make_pos ()) }
388+
| expression LBRACKET expression RBRACKET DOT IDENTIFIER MINUS_ASSIGN expression
389+
{ make_stmt (CompoundFieldIndexAssignment ($1, $3, $6, Sub, $8)) (make_pos ()) }
390+
| expression LBRACKET expression RBRACKET DOT IDENTIFIER MULTIPLY_ASSIGN expression
391+
{ make_stmt (CompoundFieldIndexAssignment ($1, $3, $6, Mul, $8)) (make_pos ()) }
392+
| expression LBRACKET expression RBRACKET DOT IDENTIFIER DIVIDE_ASSIGN expression
393+
{ make_stmt (CompoundFieldIndexAssignment ($1, $3, $6, Div, $8)) (make_pos ()) }
394+
| expression LBRACKET expression RBRACKET DOT IDENTIFIER MODULO_ASSIGN expression
395+
{ make_stmt (CompoundFieldIndexAssignment ($1, $3, $6, Mod, $8)) (make_pos ()) }
396+
383397
return_statement:
384398
| RETURN { make_stmt (Return None) (make_pos ()) }
385399
| RETURN expression { make_stmt (Return (Some $2)) (make_pos ()) }

src/safety_checker.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,10 @@ let analyze_statement_bounds stmt =
242242
errors := check_array_bounds map_expr @ !errors;
243243
errors := check_array_bounds key_expr @ !errors;
244244
errors := check_array_bounds value_expr @ !errors
245+
| CompoundFieldIndexAssignment (map_expr, key_expr, _, _, value_expr) ->
246+
errors := check_array_bounds map_expr @ !errors;
247+
errors := check_array_bounds key_expr @ !errors;
248+
errors := check_array_bounds value_expr @ !errors
245249
| FieldAssignment (obj_expr, _, value_expr) ->
246250
errors := check_array_bounds obj_expr @ !errors;
247251
errors := check_array_bounds value_expr @ !errors

src/symbol_table.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,10 @@ and process_statement table stmt =
688688
process_expression table map_expr;
689689
process_expression table key_expr;
690690
process_expression table value_expr
691+
| CompoundFieldIndexAssignment (map_expr, key_expr, _field, _, value_expr) ->
692+
process_expression table map_expr;
693+
process_expression table key_expr;
694+
process_expression table value_expr
691695
| FieldAssignment (obj_expr, _field, value_expr) ->
692696
process_expression table obj_expr;
693697
process_expression table value_expr

src/type_checker.ml

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ and typed_stmt_desc =
9494
| TAssignment of string * typed_expr
9595
| TCompoundAssignment of string * binary_op * typed_expr (* var op= expr *)
9696
| TCompoundIndexAssignment of typed_expr * typed_expr * binary_op * typed_expr (* map[key] op= expr *)
97+
| TCompoundFieldIndexAssignment of typed_expr * typed_expr * string * binary_op * typed_expr
98+
(* map[key].field op= expr *)
9799
| TFieldAssignment of typed_expr * string * typed_expr (* object, field, value *)
98100
| TArrowAssignment of typed_expr * string * typed_expr (* pointer, field, value *)
99101
| TIndexAssignment of typed_expr * typed_expr * typed_expr
@@ -1820,7 +1822,57 @@ and type_check_statement ctx stmt =
18201822
type_error ("Operator " ^ string_of_binary_op op ^
18211823
" not supported for type " ^ string_of_bpf_type element_type) stmt.stmt_pos)
18221824
| _ -> type_error ("Compound index assignment can only be used on maps or arrays") stmt.stmt_pos))
1823-
1825+
1826+
| CompoundFieldIndexAssignment (map_expr, key_expr, field, op, value_expr) ->
1827+
let typed_key = type_check_expression ctx key_expr in
1828+
let typed_value = type_check_expression ctx value_expr in
1829+
let map_name = match map_expr.expr_desc with
1830+
| Identifier name when Hashtbl.mem ctx.maps name -> name
1831+
| _ -> type_error "Compound field-index assignment requires a map identifier" stmt.stmt_pos
1832+
in
1833+
let map_decl = Hashtbl.find ctx.maps map_name in
1834+
(* Key type *)
1835+
let resolved_key_type = resolve_user_type ctx map_decl.ast_key_type in
1836+
let resolved_typed_key_type = resolve_user_type ctx typed_key.texpr_type in
1837+
(match unify_types resolved_key_type resolved_typed_key_type with
1838+
| Some _ -> ()
1839+
| None -> type_error "Map key type mismatch" stmt.stmt_pos);
1840+
(* Resolve the map's value type to a struct *)
1841+
let resolved_value_type = resolve_user_type ctx map_decl.ast_value_type in
1842+
let struct_name = match resolved_value_type with
1843+
| Struct n | UserType n -> n
1844+
| _ -> type_error "map[k].field op= rhs requires the map's value type to be a struct" stmt.stmt_pos
1845+
in
1846+
let fields =
1847+
try
1848+
(match Hashtbl.find ctx.types struct_name with
1849+
| StructDef (_, fs, _) -> fs
1850+
| _ -> type_error (struct_name ^ " is not a struct") stmt.stmt_pos)
1851+
with Not_found -> type_error ("Undefined struct: " ^ struct_name) stmt.stmt_pos
1852+
in
1853+
let field_type =
1854+
try List.assoc field fields
1855+
with Not_found ->
1856+
type_error ("Field not found: " ^ field ^ " in struct " ^ struct_name) stmt.stmt_pos
1857+
in
1858+
(* rhs must match field type *)
1859+
let resolved_field_type = resolve_user_type ctx field_type in
1860+
let resolved_typed_value_type = resolve_user_type ctx typed_value.texpr_type in
1861+
(match unify_types resolved_field_type resolved_typed_value_type with
1862+
| Some _ -> ()
1863+
| None -> type_error ("Field value type mismatch for " ^ field) stmt.stmt_pos);
1864+
(* op must be valid for the field type *)
1865+
(match op, resolved_field_type with
1866+
| (Add | Sub | Mul | Div | Mod), (U8 | U16 | U32 | U64 | I8 | I16 | I32 | I64) ->
1867+
let typed_map = { texpr_desc = TIdentifier map_name;
1868+
texpr_type = Map (map_decl.ast_key_type, map_decl.ast_value_type, map_decl.ast_map_type, map_decl.max_entries);
1869+
texpr_pos = map_expr.expr_pos } in
1870+
{ tstmt_desc = TCompoundFieldIndexAssignment (typed_map, typed_key, field, op, typed_value);
1871+
tstmt_pos = stmt.stmt_pos }
1872+
| _, _ ->
1873+
type_error ("Operator " ^ string_of_binary_op op ^
1874+
" not supported for field type " ^ string_of_bpf_type resolved_field_type) stmt.stmt_pos)
1875+
18241876
| Declaration (name, type_opt, expr_opt) ->
18251877
let typed_expr_opt = Option.map (type_check_expression ctx) expr_opt in
18261878

@@ -2600,6 +2652,8 @@ and typed_stmt_to_stmt tstmt =
26002652
| TCompoundAssignment (name, op, expr) -> CompoundAssignment (name, op, typed_expr_to_expr expr)
26012653
| TCompoundIndexAssignment (map_expr, key_expr, op, value_expr) ->
26022654
CompoundIndexAssignment (typed_expr_to_expr map_expr, typed_expr_to_expr key_expr, op, typed_expr_to_expr value_expr)
2655+
| TCompoundFieldIndexAssignment (map_expr, key_expr, field, op, value_expr) ->
2656+
CompoundFieldIndexAssignment (typed_expr_to_expr map_expr, typed_expr_to_expr key_expr, field, op, typed_expr_to_expr value_expr)
26032657
| TFieldAssignment (obj_expr, field, value_expr) ->
26042658
FieldAssignment (typed_expr_to_expr obj_expr, field, typed_expr_to_expr value_expr)
26052659
| TArrowAssignment (obj_expr, field, value_expr) ->
@@ -3272,6 +3326,13 @@ and populate_multi_program_context ast multi_prog_analysis =
32723326
(match map_expr.program_context with
32733327
| Some ctx -> map_expr.program_context <- Some { ctx with data_flow_direction = Some Write }
32743328
| None -> ())
3329+
| CompoundFieldIndexAssignment (map_expr, key_expr, _, _, value_expr) ->
3330+
enhance_expr prog_type map_expr;
3331+
enhance_expr prog_type key_expr;
3332+
enhance_expr prog_type value_expr;
3333+
(match map_expr.program_context with
3334+
| Some ctx -> map_expr.program_context <- Some { ctx with data_flow_direction = Some Write }
3335+
| None -> ())
32753336
| FieldAssignment (obj_expr, _, value_expr) ->
32763337
enhance_expr prog_type obj_expr;
32773338
enhance_expr prog_type value_expr
@@ -3377,6 +3438,10 @@ and populate_multi_program_context ast multi_prog_analysis =
33773438
enhance_userspace_expr map_expr;
33783439
enhance_userspace_expr key_expr;
33793440
enhance_userspace_expr value_expr
3441+
| CompoundFieldIndexAssignment (map_expr, key_expr, _, _, value_expr) ->
3442+
enhance_userspace_expr map_expr;
3443+
enhance_userspace_expr key_expr;
3444+
enhance_userspace_expr value_expr
33803445
| FieldAssignment (obj_expr, _, value_expr) ->
33813446
enhance_userspace_expr obj_expr;
33823447
enhance_userspace_expr value_expr

0 commit comments

Comments
 (0)