-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathuserspace_codegen.ml
More file actions
4259 lines (3742 loc) · 178 KB
/
userspace_codegen.ml
File metadata and controls
4259 lines (3742 loc) · 178 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(*
* Copyright 2025 Multikernel Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*)
(** IR-based Userspace C Code Generation
This module generates complete userspace C programs from KernelScript IR programs.
This is the unified IR-first userspace code generator.
*)
open Ir
open Printf
(** Python function call signature for bridge generation *)
type python_function_call = {
module_name: string;
function_name: string;
param_count: int;
return_type: ir_type;
}
(** Convert AST types to C types *)
let ast_type_to_c_type = function
| Ast.U8 -> "uint8_t"
| Ast.U16 -> "uint16_t"
| Ast.U32 -> "uint32_t"
| Ast.U64 -> "uint64_t"
| Ast.I8 -> "int8_t"
| Ast.I16 -> "int16_t"
| Ast.I32 -> "int32_t"
| Ast.I64 -> "int64_t"
| Ast.Bool -> "bool"
| Ast.Char -> "char"
| Ast.Void -> "void"
| _ -> "int" (* fallback for complex types *)
(** Convert IR types to C types *)
let c_type_from_ir_type = Codegen_common.ir_type_to_c Codegen_common.UserspaceStd
(** Collect Python function calls from IR programs *)
let collect_python_function_calls ir_programs resolved_imports =
let python_calls = ref [] in
(* Extract function calls from IR instructions *)
let rec extract_calls_from_instrs instrs =
List.iter (fun instr ->
match instr.instr_desc with
| IRCall (DirectCall func_name, args, ret_opt) when String.contains func_name '.' ->
(* This is a module call - check if it's Python *)
let parts = String.split_on_char '.' func_name in
(match parts with
| [module_name; function_name] ->
(* Check if this module is a Python import *)
let is_python_module = List.exists (fun import ->
import.Import_resolver.module_name = module_name &&
import.Import_resolver.source_type = Ast.Python
) resolved_imports in
if is_python_module then (
let call_signature = {
module_name = module_name;
function_name = function_name;
param_count = List.length args;
return_type = (match ret_opt with
| Some ret_val -> ret_val.val_type
| None -> IRVoid);
} in
if not (List.mem call_signature !python_calls) then
python_calls := call_signature :: !python_calls
)
| _ -> ())
| IRIf (_, then_body, else_body) ->
extract_calls_from_instrs then_body;
(match else_body with
| Some else_instrs -> extract_calls_from_instrs else_instrs
| None -> ())
| IRIfElseChain (conditions_and_bodies, final_else) ->
List.iter (fun (_, then_body) ->
extract_calls_from_instrs then_body
) conditions_and_bodies;
(match final_else with
| Some else_instrs -> extract_calls_from_instrs else_instrs
| None -> ())
| IRBpfLoop (_, _, _, _, body_instrs) ->
extract_calls_from_instrs body_instrs
| IRTry (try_instrs, catch_clauses) ->
extract_calls_from_instrs try_instrs;
List.iter (fun clause ->
extract_calls_from_instrs clause.catch_body
) catch_clauses
| _ -> ()
) instrs
in
(* Extract calls from all IR functions *)
List.iter (fun ir_func ->
List.iter (fun block ->
extract_calls_from_instrs block.instructions
) ir_func.basic_blocks
) ir_programs;
!python_calls
(** Generate bridge code for imported KernelScript and Python modules *)
let generate_mixed_bridge_code resolved_imports ir_programs =
let ks_imports = List.filter (fun import ->
match import.Import_resolver.source_type with
| Ast.KernelScript -> true
| _ -> false
) resolved_imports in
let py_imports = List.filter (fun import ->
match import.Import_resolver.source_type with
| Ast.Python -> true
| _ -> false
) resolved_imports in
(* Generate KernelScript bridge code *)
let ks_bridge_code = if ks_imports = [] then ""
else
let ks_declarations = List.map (fun import ->
let module_name = import.Import_resolver.module_name in
let function_decls = List.map (fun symbol ->
match symbol.Import_resolver.symbol_type with
| Ast.Function (param_types, return_type) ->
let c_return_type = ast_type_to_c_type return_type in
let c_param_types = List.map ast_type_to_c_type param_types in
let params_str = if c_param_types = [] then "void" else String.concat ", " c_param_types in
sprintf "extern %s %s_%s(%s);" c_return_type module_name symbol.symbol_name params_str
| _ ->
sprintf "// %s (non-function symbol)" symbol.symbol_name
) import.ks_symbols in
sprintf "// External functions from KernelScript module: %s\n%s" module_name (String.concat "\n" function_decls)
) ks_imports in
sprintf "\n// Bridge code for imported KernelScript modules\n%s\n" (String.concat "\n\n" ks_declarations)
in
(* Generate Python bridge code based on actual function calls *)
let py_bridge_code = if py_imports = [] then ""
else
(* Collect actual Python function calls from IR *)
let python_calls = collect_python_function_calls ir_programs resolved_imports in
if python_calls = [] then
(* No Python function calls found - generate minimal bridge *)
let py_headers = "\n#include <Python.h>" in
let py_minimal_bridge = List.map (fun import ->
let module_name = import.Import_resolver.module_name in
let file_path = import.Import_resolver.resolved_path in
let python_module_name = Filename.remove_extension (Filename.basename file_path) in
sprintf {|
// Python module: %s
static PyObject* %s_module = NULL;
// Initialize Python bridge for %s
int init_%s_bridge(void) {
if (!Py_IsInitialized()) {
Py_Initialize();
if (!Py_IsInitialized()) {
fprintf(stderr, "Failed to initialize Python interpreter\n");
return -1;
}
}
// Add the current directory to Python path
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.insert(0, '.')");
// Import the module by name
PyObject* module_name_obj = PyUnicode_FromString("%s");
if (!module_name_obj) {
fprintf(stderr, "Failed to create module name string\n");
return -1;
}
%s_module = PyImport_Import(module_name_obj);
Py_DECREF(module_name_obj);
if (!%s_module) {
PyErr_Print();
fprintf(stderr, "Failed to import Python module: %s (make sure %s.py is in the current directory)\n");
return -1;
}
return 0;
}
// Cleanup Python bridge for %s
void cleanup_%s_bridge(void) {
if (%s_module) {
Py_DECREF(%s_module);
%s_module = NULL;
}
}|} module_name module_name module_name module_name python_module_name
module_name module_name module_name python_module_name
module_name module_name module_name module_name module_name
) py_imports in
sprintf "%s\n// Bridge code for imported Python modules\n%s\n" py_headers (String.concat "\n\n" py_minimal_bridge)
else
(* Generate specific bridge functions for actual calls *)
let py_headers = "\n#include <Python.h>" in
(* Group calls by module *)
let calls_by_module = List.fold_left (fun acc call ->
let existing_calls = try List.assoc call.module_name acc with Not_found -> [] in
let updated_calls = call :: (List.filter (fun c -> c.function_name <> call.function_name) existing_calls) in
(call.module_name, updated_calls) :: (List.remove_assoc call.module_name acc)
) [] python_calls in
let py_declarations = List.map (fun import ->
let module_name = import.Import_resolver.module_name in
let file_path = import.Import_resolver.resolved_path in
let python_module_name = Filename.remove_extension (Filename.basename file_path) in
(* Get the calls for this module *)
let module_calls = try List.assoc module_name calls_by_module with Not_found -> [] in
(* Generate bridge functions for each called function *)
let bridge_functions = List.map (fun call ->
let c_return_type = c_type_from_ir_type call.return_type in
let params_list = List.init call.param_count (fun i -> sprintf "PyObject* arg%d" i) in
let params_str = if params_list = [] then "void" else String.concat ", " params_list in
let args_tuple = if call.param_count = 0 then "NULL" else (
let arg_refs = List.init call.param_count (fun i -> sprintf "arg%d" i) in
sprintf "Py_BuildValue(\"(%s)\", %s)"
(String.make call.param_count 'O')
(String.concat ", " arg_refs)
) in
sprintf {|
// Bridge function for %s.%s
%s %s_%s(%s) {
if (!%s_module) {
fprintf(stderr, "Python module %s not initialized\n");
return (%s){0};
}
PyObject* py_func = PyObject_GetAttrString(%s_module, "%s");
if (!py_func || !PyCallable_Check(py_func)) {
fprintf(stderr, "Function %s not found in module %s\n");
Py_XDECREF(py_func);
return (%s){0};
}
PyObject* args_tuple = %s;
PyObject* result = PyObject_CallObject(py_func, args_tuple);
Py_DECREF(py_func);
if (args_tuple) Py_DECREF(args_tuple);
if (!result) {
PyErr_Print();
return (%s){0};
}
%s ret_val = %s;
if (PyErr_Occurred()) {
PyErr_Print();
Py_DECREF(result);
return (%s){0};
}
Py_DECREF(result);
return ret_val;
}|} module_name call.function_name c_return_type module_name call.function_name params_str
module_name module_name c_return_type module_name call.function_name call.function_name
module_name c_return_type args_tuple c_return_type c_return_type
(match call.return_type with
| IRU64 -> "PyLong_AsUnsignedLongLong(result)"
| IRU32 -> "(uint32_t)PyLong_AsUnsignedLong(result)"
| IRU16 -> "(uint16_t)PyLong_AsUnsignedLong(result)"
| IRU8 -> "(uint8_t)PyLong_AsUnsignedLong(result)"
| IRI64 -> "PyLong_AsLongLong(result)"
| IRI32 -> "(int32_t)PyLong_AsLong(result)"
| IRI16 -> "(int16_t)PyLong_AsLong(result)"
| IRI8 -> "(int8_t)PyLong_AsLong(result)"
| IRBool -> "PyObject_IsTrue(result)"
| IRF64 -> "PyFloat_AsDouble(result)"
| IRF32 -> "(float)PyFloat_AsDouble(result)"
| IRStr _ -> "/* string conversion would go here */"
| _ -> "0 /* unsupported type */") c_return_type
) module_calls in
sprintf {|
// Python module: %s
static PyObject* %s_module = NULL;
%s
// Initialize Python bridge for %s
int init_%s_bridge(void) {
if (!Py_IsInitialized()) {
Py_Initialize();
if (!Py_IsInitialized()) {
fprintf(stderr, "Failed to initialize Python interpreter\n");
return -1;
}
}
// Add the current directory to Python path
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.insert(0, '.')");
// Import the module by name
PyObject* module_name_obj = PyUnicode_FromString("%s");
if (!module_name_obj) {
fprintf(stderr, "Failed to create module name string\n");
return -1;
}
%s_module = PyImport_Import(module_name_obj);
Py_DECREF(module_name_obj);
if (!%s_module) {
PyErr_Print();
fprintf(stderr, "Failed to import Python module: %s (make sure %s.py is in the current directory)\n");
return -1;
}
return 0;
}
// Cleanup Python bridge for %s
void cleanup_%s_bridge(void) {
if (%s_module) {
Py_DECREF(%s_module);
%s_module = NULL;
}
}|} module_name module_name (String.concat "\n" bridge_functions) module_name module_name
python_module_name module_name module_name module_name python_module_name
module_name module_name module_name module_name module_name
) py_imports in
sprintf "%s\n// Bridge code for imported Python modules\n%s\n" py_headers (String.concat "\n\n" py_declarations)
in
ks_bridge_code ^ py_bridge_code
(** Generate Python initialization calls for all Python imports *)
let generate_python_initialization_calls resolved_imports =
let py_imports = List.filter (fun import ->
match import.Import_resolver.source_type with
| Ast.Python -> true
| _ -> false
) resolved_imports in
if py_imports = [] then ""
else
let init_calls = List.map (fun import ->
let module_name = import.Import_resolver.module_name in
sprintf " if (init_%s_bridge() != 0) {\n fprintf(stderr, \"Failed to initialize Python module: %s\\n\");\n return 1;\n }" module_name module_name
) py_imports in
sprintf "\n // Initialize Python modules\n%s\n"
(String.concat "\n" init_calls)
(** Dependency information for a single eBPF program *)
type program_dependencies = {
program_name: string;
program_type: string; (* xdp, tc, kprobe, etc *)
required_kfuncs: string list;
required_modules: string list;
}
(** System-wide kfunc dependency information *)
type kfunc_dependency_info = {
kfunc_definitions: (string * Ast.function_def) list; (* kfunc_name -> function_def *)
private_functions: (string * Ast.function_def) list; (* private function_name -> function_def *)
program_dependencies: program_dependencies list;
module_name: string;
}
(** Function usage tracking for optimization *)
type function_usage = {
mutable uses_load: bool;
mutable uses_attach: bool;
mutable uses_detach: bool;
mutable uses_map_operations: bool;
mutable uses_daemon: bool;
mutable uses_exec: bool;
mutable used_maps: string list;
mutable used_dispatch_functions: int list;
}
let create_function_usage () = {
uses_load = false;
uses_attach = false;
uses_detach = false;
uses_map_operations = false;
uses_daemon = false;
uses_exec = false;
used_maps = [];
used_dispatch_functions = [];
}
(** Extract kfunc and private function definitions from AST *)
let extract_kfunc_and_private_functions ast =
let kfuncs = ref [] in
let privates = ref [] in
List.iter (function
| Ast.AttributedFunction attr_func ->
let is_kfunc = List.exists (function
| Ast.SimpleAttribute "kfunc" -> true
| _ -> false
) attr_func.attr_list in
let is_private = List.exists (function
| Ast.SimpleAttribute "private" -> true
| _ -> false
) attr_func.attr_list in
if is_kfunc then
kfuncs := (attr_func.attr_function.func_name, attr_func.attr_function) :: !kfuncs
else if is_private then
privates := (attr_func.attr_function.func_name, attr_func.attr_function) :: !privates
| _ -> ()
) ast;
(!kfuncs, !privates)
(** Extract function calls from IR instructions *)
let rec extract_function_calls_from_ir_instrs instrs =
let calls = ref [] in
List.iter (fun instr ->
match instr.instr_desc with
| IRCall (target, _, _) ->
(match target with
| DirectCall func_name -> calls := func_name :: !calls
| FunctionPointerCall _ -> ())
| IRIf (_, then_body, else_body) ->
calls := (extract_function_calls_from_ir_instrs then_body) @ !calls;
(match else_body with
| Some else_instrs -> calls := (extract_function_calls_from_ir_instrs else_instrs) @ !calls
| None -> ())
| IRIfElseChain (conditions_and_bodies, final_else) ->
List.iter (fun (_, then_body) ->
calls := (extract_function_calls_from_ir_instrs then_body) @ !calls
) conditions_and_bodies;
(match final_else with
| Some else_instrs -> calls := (extract_function_calls_from_ir_instrs else_instrs) @ !calls
| None -> ())
| IRBpfLoop (_, _, _, _, body_instrs) ->
calls := (extract_function_calls_from_ir_instrs body_instrs) @ !calls
| IRTry (try_instrs, catch_clauses) ->
calls := (extract_function_calls_from_ir_instrs try_instrs) @ !calls;
List.iter (fun clause ->
calls := (extract_function_calls_from_ir_instrs clause.catch_body) @ !calls
) catch_clauses
| _ -> ()
) instrs;
!calls
(** Extract function calls from an IR function *)
let extract_function_calls_from_ir_function ir_func =
List.fold_left (fun acc block ->
acc @ (extract_function_calls_from_ir_instrs block.instructions)
) [] ir_func.basic_blocks
(** Determine program type from function attributes *)
let get_program_type_from_attributes attr_list =
List.fold_left (fun acc attr ->
match attr with
| Ast.SimpleAttribute attr_name when List.mem attr_name ["xdp"; "tc"; "kprobe"; "tracepoint"] ->
Some attr_name
| _ -> acc
) None attr_list
(** Extract eBPF program information from AST *)
let extract_ebpf_programs ast =
List.filter_map (function
| Ast.AttributedFunction attr_func ->
(match get_program_type_from_attributes attr_func.attr_list with
| Some prog_type ->
Some (attr_func.attr_function.func_name, prog_type)
| None -> None)
| _ -> None
) ast
(** Analyze kfunc dependencies for all eBPF programs *)
let analyze_kfunc_dependencies module_name ast ir_programs =
let (kfunc_definitions, private_functions) = extract_kfunc_and_private_functions ast in
let ebpf_programs = extract_ebpf_programs ast in
let kfunc_names = List.map fst kfunc_definitions in
(* For each eBPF program, find which kfuncs it calls *)
let program_dependencies = List.filter_map (fun (prog_name, prog_type) ->
(* Find the corresponding IR function *)
match List.find_opt (fun ir_func -> ir_func.func_name = prog_name) ir_programs with
| Some ir_func ->
let all_calls = extract_function_calls_from_ir_function ir_func in
(* Filter to only kfunc calls *)
let kfunc_calls = List.filter (fun call_name ->
List.mem call_name kfunc_names
) all_calls in
if kfunc_calls <> [] then
(* Remove duplicates *)
let unique_kfuncs = List.sort_uniq String.compare kfunc_calls in
Some {
program_name = prog_name;
program_type = prog_type;
required_kfuncs = unique_kfuncs;
required_modules = [module_name]; (* Currently all kfuncs are in one module *)
}
else
None
| None -> None
) ebpf_programs in
{
kfunc_definitions;
private_functions;
program_dependencies;
module_name;
}
(** Check if any eBPF programs have kfunc dependencies *)
let has_kfunc_dependencies dependency_info =
dependency_info.program_dependencies <> []
(** Generate kernel module loading code for userspace *)
let generate_kmodule_loading_code dependency_info =
if dependency_info.program_dependencies = [] then
""
else
let program_checks = String.concat "\n" (List.map (fun prog_dep ->
let module_loads = String.concat "\n " (List.map (fun module_name ->
sprintf {|if (load_kernel_module("%s") != 0) return -1;|} module_name
) prog_dep.required_modules) in
sprintf {| if (strcmp(program_name, "%s") == 0) {
/* Program %s requires modules: %s */
%s
}|}
prog_dep.program_name
prog_dep.program_name
(String.concat ", " prog_dep.required_modules)
module_loads
) dependency_info.program_dependencies) in
sprintf {|
/* Kernel module loading for kfunc dependencies */
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#ifndef __NR_finit_module
#define __NR_finit_module 313
#endif
static int finit_module(int fd, const char *param_values, int flags) {
return syscall(__NR_finit_module, fd, param_values, flags);
}
static int load_kernel_module(const char *module_name) {
char module_path[256];
snprintf(module_path, sizeof(module_path), "%%s.mod.ko", module_name);
/* Open the kernel module file */
int fd = open(module_path, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT) {
printf("Warning: Kernel module file %%s not found (may already be loaded)\n", module_path);
return 0; /* Don't fail - module might already be loaded or available via modprobe */
}
printf("Failed to open kernel module file %%s: %%s\n", module_path, strerror(errno));
return -1;
}
/* Load the module using finit_module syscall */
int ret = finit_module(fd, "", 0);
close(fd);
if (ret == 0) {
printf("Loaded kernel module: %%s\n", module_name);
return 0;
} else {
if (errno == EEXIST) {
printf("Kernel module %%s already loaded\n", module_name);
return 0; /* Module already loaded - this is fine */
} else if (errno == EPERM) {
printf("Permission denied loading kernel module %%s (try running as root)\n", module_name);
return -1;
} else {
printf("Warning: Failed to load kernel module %%s: %%s (may already be loaded)\n", module_name, strerror(errno));
return 0; /* Don't fail - module might be loaded via different means */
}
}
}
static int ensure_kfunc_dependencies_loaded(const char *program_name) {
/* Check which modules this program depends on */
%s
return 0;
}
|} program_checks
(** Context for C code generation *)
type userspace_context = {
temp_counter: int ref;
function_name: string;
is_main: bool;
(* Track register to variable name mapping for better C code *)
register_vars: (int, string) Hashtbl.t;
(* Track variable declarations needed - elegant IR-based approach *)
var_declarations: (string, ir_type) Hashtbl.t; (* var_name -> ir_type *)
(* Track IR values for elegant variable naming *)
ir_var_values: (string, ir_value) Hashtbl.t; (* var_name -> ir_value *)
(* Track variables declared via IRVariableDecl instructions *)
declared_via_ir: (string, unit) Hashtbl.t; (* var_name -> unit *)
(* Track function usage for optimization *)
function_usage: function_usage;
(* Global variables for skeleton access *)
global_variables: ir_global_variable list;
mutable inlinable_registers: (int, string) Hashtbl.t;
mutable current_function: ir_function option;
(* Ring buffer event handler registrations *)
ring_buffer_handlers: (string, string) Hashtbl.t; (* map_name -> handler_function_name *)
function_parameters: (string, unit) Hashtbl.t; (* param_name -> unit *)
(* Pre-computed variable naming decisions *)
needs_var_prefix: (string, unit) Hashtbl.t; (* var_name -> unit *)
}
let create_context_base ?(global_variables = []) ~function_name ~is_main () = {
temp_counter = ref 0;
function_name;
is_main;
register_vars = Hashtbl.create 32;
var_declarations = Hashtbl.create 32;
ir_var_values = Hashtbl.create 32;
declared_via_ir = Hashtbl.create 32;
function_usage = create_function_usage ();
global_variables;
inlinable_registers = Hashtbl.create 32;
current_function = None;
ring_buffer_handlers = Hashtbl.create 16;
function_parameters = Hashtbl.create 16;
needs_var_prefix = Hashtbl.create 32;
}
let create_userspace_context ?(global_variables = []) () =
create_context_base ~global_variables ~function_name:"user_function" ~is_main:false ()
let create_main_context ?(global_variables = []) () =
create_context_base ~global_variables ~function_name:"main" ~is_main:true ()
(** C reserved keywords that need to be avoided *)
let c_reserved_keywords = [
"auto"; "break"; "case"; "char"; "const"; "continue"; "default"; "do";
"double"; "else"; "enum"; "extern"; "float"; "for"; "goto"; "if";
"inline"; "int"; "long"; "register"; "restrict"; "return"; "short"; "signed";
"sizeof"; "static"; "struct"; "switch"; "typedef"; "union"; "unsigned";
"void"; "volatile"; "while"; "_Bool"; "_Complex"; "_Imaginary";
(* Common POSIX and system identifiers *)
"stdin"; "stdout"; "stderr"; "errno"; "NULL"
]
let generate_c_var_name ctx ir_value =
match ir_value.value_desc with
| IRVariable name ->
if Hashtbl.mem ctx.needs_var_prefix name then
let base_name = if List.mem name c_reserved_keywords then name ^ "_var" else name in
"var_" ^ base_name
else
(* Function parameters and globals use original names *)
if List.mem name c_reserved_keywords then name ^ "_var" else name
| IRTempVariable name ->
(* Compiler-generated temporaries use their names directly *)
if List.mem name c_reserved_keywords then name ^ "_var" else name
| _ ->
(* For other value types, this function shouldn't be called *)
failwith "generate_c_var_name called on non-variable IR value"
let sanitize_var_name var_name =
(* This is a fallback for cases where we only have the name string *)
(* In an ideal world, this function would be eliminated entirely *)
if List.mem var_name c_reserved_keywords then
var_name ^ "_var"
else
var_name
let fresh_temp_var ctx prefix =
incr ctx.temp_counter;
sprintf "%s_%d" prefix !(ctx.temp_counter)
(** Track function usage based on instruction *)
let track_function_usage ctx instr =
match instr.instr_desc with
| IRCall (target, args, _) ->
(match target with
| DirectCall func_name ->
(match func_name with
| "load" -> ctx.function_usage.uses_load <- true
| "attach" -> ctx.function_usage.uses_attach <- true
| "detach" -> ctx.function_usage.uses_detach <- true
| "daemon" -> ctx.function_usage.uses_daemon <- true
| "exec" ->
ctx.function_usage.uses_exec <- true
| "dispatch" ->
let num_buffers = List.length args in
if not (List.mem num_buffers ctx.function_usage.used_dispatch_functions) then
ctx.function_usage.used_dispatch_functions <- num_buffers :: ctx.function_usage.used_dispatch_functions
| _ -> ())
| FunctionPointerCall _ -> ())
| IRMapLoad (map_val, _, _, _)
| IRMapStore (map_val, _, _, _)
| IRMapDelete (map_val, _) ->
ctx.function_usage.uses_map_operations <- true;
(match map_val.value_desc with
| IRMapRef map_name ->
if not (List.mem map_name ctx.function_usage.used_maps) then
ctx.function_usage.used_maps <- map_name :: ctx.function_usage.used_maps
| _ -> ())
| IRConfigFieldUpdate (map_val, _, _, _) ->
ctx.function_usage.uses_map_operations <- true;
(match map_val.value_desc with
| IRMapRef map_name ->
if not (List.mem map_name ctx.function_usage.used_maps) then
ctx.function_usage.used_maps <- map_name :: ctx.function_usage.used_maps
| _ -> ())
| IRConfigAccess (config_name, _, _) ->
(* Track config access as map operations since configs are implemented as maps *)
ctx.function_usage.uses_map_operations <- true;
let config_map_name = config_name ^ "_config" in
if not (List.mem config_map_name ctx.function_usage.used_maps) then
ctx.function_usage.used_maps <- config_map_name :: ctx.function_usage.used_maps
| IRStructOpsRegister (_, _) ->
(* Struct_ops registration requires skeleton object to be loaded *)
ctx.function_usage.uses_attach <- true
| IRRingbufOp (_, _) ->
(* Ring buffer operations require skeleton and ring buffer setup *)
ctx.function_usage.uses_map_operations <- true
| _ -> ()
(** Recursively track usage in all instructions *)
let rec track_usage_in_instructions ctx instrs =
List.iter (fun instr ->
track_function_usage ctx instr;
match instr.instr_desc with
| IRIf (_, then_body, else_body) ->
track_usage_in_instructions ctx then_body;
(match else_body with
| Some else_instrs -> track_usage_in_instructions ctx else_instrs
| None -> ())
| IRIfElseChain (conditions_and_bodies, final_else) ->
List.iter (fun (_, then_body) ->
track_usage_in_instructions ctx then_body
) conditions_and_bodies;
(match final_else with
| Some else_instrs -> track_usage_in_instructions ctx else_instrs
| None -> ())
| IRBpfLoop (_, _, _, _, body_instrs) ->
track_usage_in_instructions ctx body_instrs
| IRTry (try_instrs, catch_clauses) ->
track_usage_in_instructions ctx try_instrs;
List.iter (fun clause ->
track_usage_in_instructions ctx clause.catch_body
) catch_clauses
| _ -> ()
) instrs
(* Removed unused string size collection functions *)
(** Collect string sizes from IR - but only those used in concatenation operations *)
let rec collect_string_concat_sizes_from_ir_expr ir_expr =
match ir_expr.expr_desc with
| IRValue _ir_value -> [] (* Values alone don't need concatenation helpers *)
| IRBinOp (left, op, right) ->
(* Only collect sizes for string concatenation operations *)
(match left.val_type, op, right.val_type with
| IRStr _, IRAdd, IRStr _ ->
(* This is a string concatenation - collect the result size *)
(match ir_expr.expr_type with
| IRStr result_size -> [result_size]
| _ -> [])
| _ -> []) (* Other binary operations don't need concatenation helpers *)
| IRUnOp (_, _operand) -> [] (* Unary operations don't need concatenation helpers *)
| IRCast (_value, _target_type) -> [] (* Casts don't need concatenation helpers *)
| IRFieldAccess (_obj, _) -> [] (* Field access doesn't need concatenation helpers *)
| IRStructLiteral (_, field_assignments) ->
List.fold_left (fun acc (_, field_val) ->
acc @ (collect_string_concat_sizes_from_ir_value field_val)
) [] field_assignments
| IRMatch (matched_val, arms) ->
(* Collect string sizes from matched expression and all arms *)
(collect_string_concat_sizes_from_ir_value matched_val) @
(List.fold_left (fun acc arm ->
acc @ (collect_string_concat_sizes_from_ir_value arm.ir_arm_value)
) [] arms)
and collect_string_concat_sizes_from_ir_value ir_value =
match ir_value.value_desc with
| IRLiteral _ -> [] (* Literals alone don't need concatenation helpers *)
| _ -> [] (* Other values don't need concatenation helpers *)
let rec collect_string_concat_sizes_from_ir_instruction ir_instr =
match ir_instr.instr_desc with
| IRAssign (_dest, expr) ->
(* Only collect from expressions that involve concatenation *)
collect_string_concat_sizes_from_ir_expr expr
| IRVariableDecl (_dest_val, _typ, init_expr_opt) ->
(match init_expr_opt with
| Some init_expr -> collect_string_concat_sizes_from_ir_expr init_expr
| None -> [])
| IRCall (_, _args, _ret_opt) -> [] (* Function calls don't need concatenation helpers *)
| IRReturn value_opt ->
(match value_opt with
| Some value -> collect_string_concat_sizes_from_ir_value value
| None -> [])
| IRIf (_cond, then_body, else_body) ->
let then_sizes = List.fold_left (fun acc instr ->
acc @ (collect_string_concat_sizes_from_ir_instruction instr)
) [] then_body in
let else_sizes = match else_body with
| Some else_instrs -> List.fold_left (fun acc instr ->
acc @ (collect_string_concat_sizes_from_ir_instruction instr)
) [] else_instrs
| None -> []
in
then_sizes @ else_sizes
| IRIfElseChain (conditions_and_bodies, final_else) ->
let chain_sizes = List.fold_left (fun acc (_cond, then_body) ->
acc @ (List.fold_left (fun acc2 instr ->
acc2 @ (collect_string_concat_sizes_from_ir_instruction instr)
) [] then_body)
) [] conditions_and_bodies in
let final_sizes = match final_else with
| Some else_instrs -> List.fold_left (fun acc instr ->
acc @ (collect_string_concat_sizes_from_ir_instruction instr)
) [] else_instrs
| None -> []
in
chain_sizes @ final_sizes
| IRBpfLoop (_, _, _, _, body_instrs) ->
List.fold_left (fun acc instr ->
acc @ (collect_string_concat_sizes_from_ir_instruction instr)
) [] body_instrs
| IRTry (try_instrs, catch_clauses) ->
let try_sizes = List.fold_left (fun acc instr ->
acc @ (collect_string_concat_sizes_from_ir_instruction instr)
) [] try_instrs in
let catch_sizes = List.fold_left (fun acc clause ->
acc @ (List.fold_left (fun acc2 instr ->
acc2 @ (collect_string_concat_sizes_from_ir_instruction instr)
) [] clause.catch_body)
) [] catch_clauses in
try_sizes @ catch_sizes
| _ -> [] (* Other instruction types don't involve concatenation *)
and collect_string_concat_sizes_from_ir_function ir_func =
List.fold_left (fun acc block ->
List.fold_left (fun acc2 instr ->
acc2 @ (collect_string_concat_sizes_from_ir_instruction instr)
) acc block.instructions
) [] ir_func.basic_blocks
and collect_string_concat_sizes_from_userspace_program userspace_prog =
List.fold_left (fun acc func ->
acc @ (collect_string_concat_sizes_from_ir_function func)
) [] userspace_prog.userspace_functions
(** Collect enum definitions from IR types *)
let collect_enum_definitions_from_userspace userspace_prog =
let enum_map = Hashtbl.create 16 in
let rec collect_from_type = function
| IREnum (name, values) ->
(* Note: Enum filtering is now handled at the IR level based on source file *)
Hashtbl.replace enum_map name values
| IRPointer (inner_type, _) -> collect_from_type inner_type
| IRArray (inner_type, _, _) -> collect_from_type inner_type
| IRResult (ok_type, err_type) ->
collect_from_type ok_type; collect_from_type err_type
| _ -> ()
in
let collect_from_value ir_val =
collect_from_type ir_val.val_type;
(* Also collect from enum constants *)
(match ir_val.value_desc with
| IREnumConstant (enum_name, constant_name, value) ->
(* Note: Enum constant filtering is now handled at the IR level based on source file *)
let current_values = try Hashtbl.find enum_map enum_name with Not_found -> [] in
let updated_values = (constant_name, value) :: (List.filter (fun (name, _) -> name <> constant_name) current_values) in
Hashtbl.replace enum_map enum_name updated_values
| _ -> ())
in
let collect_from_expr ir_expr =
match ir_expr.expr_desc with
| IRValue ir_val -> collect_from_value ir_val
| IRBinOp (left, _, right) ->
collect_from_value left; collect_from_value right
| IRUnOp (_, ir_val) -> collect_from_value ir_val
| IRCast (ir_val, target_type) ->
collect_from_value ir_val; collect_from_type target_type
| IRFieldAccess (obj_val, _) -> collect_from_value obj_val
| IRStructLiteral (_, field_assignments) ->
List.iter (fun (_, field_val) -> collect_from_value field_val) field_assignments
| IRMatch (matched_val, arms) ->
(* Collect from matched expression and all arms *)
collect_from_value matched_val;
List.iter (fun arm -> collect_from_value arm.ir_arm_value) arms
in
let rec collect_from_instr ir_instr =
match ir_instr.instr_desc with
| IRAssign (dest_val, expr) ->
collect_from_value dest_val; collect_from_expr expr
| IRVariableDecl (_dest_val, _typ, init_expr_opt) ->
(match init_expr_opt with
| Some init_expr -> collect_from_expr init_expr
| None -> ())
| IRCall (_, args, ret_opt) ->
List.iter collect_from_value args;
(match ret_opt with Some ret_val -> collect_from_value ret_val | None -> ())
| IRMapLoad (map_val, key_val, dest_val, _) ->
collect_from_value map_val; collect_from_value key_val; collect_from_value dest_val
| IRMapStore (map_val, key_val, value_val, _) ->
collect_from_value map_val; collect_from_value key_val; collect_from_value value_val
| IRReturn (Some ret_val) -> collect_from_value ret_val
| IRMatchReturn (matched_val, arms) ->
collect_from_value matched_val;
List.iter (fun arm ->
(match arm.match_pattern with
| IRConstantPattern const_val -> collect_from_value const_val
| IRDefaultPattern -> ());
(match arm.return_action with
| IRReturnValue ret_val -> collect_from_value ret_val
| IRReturnCall (_, args) -> List.iter collect_from_value args
| IRReturnTailCall (_, args, _) -> List.iter collect_from_value args)
) arms
| IRIf (cond_val, then_instrs, else_instrs_opt) ->
collect_from_value cond_val;
List.iter collect_from_instr then_instrs;
(match else_instrs_opt with Some instrs -> List.iter collect_from_instr instrs | None -> ())
| IRIfElseChain (conditions_and_bodies, final_else) ->
List.iter (fun (cond_val, then_instrs) ->
collect_from_value cond_val;
List.iter collect_from_instr then_instrs
) conditions_and_bodies;
(match final_else with Some instrs -> List.iter collect_from_instr instrs | None -> ())
| _ -> ()
in
let collect_from_function ir_func =
List.iter (fun block ->
List.iter collect_from_instr block.instructions
) ir_func.basic_blocks
in
(* Collect from struct fields *)
List.iter (fun struct_def ->
List.iter (fun (_field_name, field_type) ->
collect_from_type field_type
) struct_def.struct_fields
) userspace_prog.userspace_structs;
(* Collect from all userspace functions *)
List.iter collect_from_function userspace_prog.userspace_functions;
enum_map
(** Generate enum definition *)
let generate_enum_definition_userspace enum_name enum_values =
let value_count = List.length enum_values in
let enum_variants = List.mapi (fun i (const_name, value) ->
let line = sprintf " %s = %s%s" const_name (Ast.IntegerValue.to_string value) (if i = value_count - 1 then "" else ",") in
line
) enum_values in
sprintf "enum %s {\n%s\n};" enum_name (String.concat "\n" enum_variants)
(** Generate all enum definitions for userspace *)
let generate_enum_definitions_userspace userspace_prog =
let enum_map = collect_enum_definitions_from_userspace userspace_prog in
if Hashtbl.length enum_map > 0 then (
(* Kernel enums never appear in userspace when using includes *)
let user_defined_enums = Hashtbl.fold (fun enum_name enum_values acc ->
(enum_name, enum_values) :: acc
) enum_map [] in
if List.length user_defined_enums > 0 then (
let enum_defs = List.map (fun (enum_name, enum_values) ->
generate_enum_definition_userspace enum_name enum_values
) user_defined_enums in
"/* Enum definitions */\n" ^ (String.concat "\n\n" enum_defs) ^ "\n\n"
) else ""
) else ""