diff --git a/IR/CMakeLists.txt b/IR/CMakeLists.txt index fac908ff..fe62baba 100644 --- a/IR/CMakeLists.txt +++ b/IR/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable( aux/eliminate_ssa.c aux/conflict_analysis.c aux/graph_coloring.c + aux/explicit_reg.c ir_code_gen.c ) diff --git a/IR/aux/conflict_analysis.c b/IR/aux/conflict_analysis.c index d69aec57..3a7727f3 100644 --- a/IR/aux/conflict_analysis.c +++ b/IR/aux/conflict_analysis.c @@ -4,6 +4,7 @@ #include "code_gen.h" #include "dbg.h" #include "ir_helper.h" +#include "list.h" int is_final(struct ir_insn *v1) { return v1 == dst(v1); @@ -62,23 +63,21 @@ void conflict_analysis(struct ir_function *fun) { struct ir_basic_block **pos; // For each BB array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_bb_cg_extra *bb_cg = bb->user_data; - struct ir_insn **pos2; - array_for(pos2, bb_cg->out) { - struct ir_insn *insn_dst = dst(*pos2); - // Add the variable to the "all variable set" - array_push_unique(&fun->cg_info.all_var, &insn_dst); - } - array_for(pos2, bb_cg->kill) { - struct ir_insn *insn_dst = dst(*pos2); - array_push_unique(&fun->cg_info.all_var, &insn_dst); - struct ir_insn **pos3; - array_for(pos3, bb_cg->out) { - build_conflict(insn_dst, dst(*pos3)); - } - array_for(pos3, bb_cg->kill) { - build_conflict(insn_dst, dst(*pos3)); + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + // For each operation + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_insn **pos2; + struct ir_insn_cg_extra *insn_cg = insn->user_data; + array_for(pos2, insn_cg->kill) { + struct ir_insn *insn_dst = *pos2; + DBGASSERT(insn_dst == dst(insn_dst)); + array_push_unique(&fun->cg_info.all_var, &insn_dst); + struct ir_insn **pos3; + array_for(pos3, insn_cg->out) { + DBGASSERT(*pos3 == dst(*pos3)); + build_conflict(insn_dst, *pos3); + } } } } diff --git a/IR/aux/eliminate_ssa.c b/IR/aux/eliminate_ssa.c index bdfcdba1..9bc1bccd 100644 --- a/IR/aux/eliminate_ssa.c +++ b/IR/aux/eliminate_ssa.c @@ -82,6 +82,8 @@ void remove_phi(struct ir_function *fun) { CRITICAL("Empty Phi not removed!"); } + DBGASSERT(repr == dst(repr)); + replace_all_usage(insn, ir_value_insn(repr)); erase_insn(insn); } diff --git a/IR/aux/explicit_reg.c b/IR/aux/explicit_reg.c new file mode 100644 index 00000000..9ed014bc --- /dev/null +++ b/IR/aux/explicit_reg.c @@ -0,0 +1,65 @@ +// Make register usage explicit +// Example: +// %x = add %y, %arg1 +// arg1 is r0 at the beginning of the function +// We then add a new instruction to the beginning of the function. + +#include +#include "array.h" +#include "bpf_ir.h" +#include "code_gen.h" +#include "dbg.h" +#include "ir_insn.h" +#include "ir_helper.h" + +void explicit_reg(struct ir_function *fun) { + // fun is still in IR form + // Before this step, users are correct + // In this step we change some dsts + // We need carefully handle the users + // dsts are NOT users + // Invariant: All operands are final values + // Final value: v == dst(v) + struct ir_basic_block **pos; + // Maximum number of functions: MAX_FUNC_ARG + struct array call_insns = INIT_ARRAY(struct ir_insn *); + array_for(pos, fun->reachable_bbs) { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + if (insn->op == IR_INSN_CALL) { + array_push(&call_insns, &insn); + } + } + } + // Functions that are called + struct ir_insn **pos2; + array_for(pos2, call_insns) { + struct ir_insn *insn = *pos2; + for (__u8 i = 0; i < insn->value_num; ++i) { + struct ir_value val = insn->values[i]; + struct ir_insn *new_insn = create_assign_insn_cg(insn, val, INSERT_FRONT); + insn_cg(new_insn)->dst = fun->cg_info.regs[i + 1]; + val_remove_user(val, insn); + } + insn->value_num = 0; // Remove all operands + struct ir_insn_cg_extra *extra = insn_cg(insn); + extra->dst = NULL; + if (insn->users.num_elem == 0) { + continue; + } + struct ir_insn *new_insn = + create_assign_insn_cg(insn, ir_value_insn(fun->cg_info.regs[0]), INSERT_BACK); + replace_all_usage(insn, ir_value_insn(new_insn)); + } + array_free(&call_insns); + // Arg + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + if (fun->function_arg[i]->users.num_elem > 0) { + // Insert ASSIGN arg[i] at the beginning of the function + struct ir_insn *new_insn = create_assign_insn_bb_cg( + fun->entry, ir_value_insn(fun->cg_info.regs[i + 1]), INSERT_FRONT_AFTER_PHI); + replace_all_usage(fun->function_arg[i], ir_value_insn(new_insn)); + } + } +} diff --git a/IR/aux/graph_coloring.c b/IR/aux/graph_coloring.c index e81c420d..5ebe51a4 100644 --- a/IR/aux/graph_coloring.c +++ b/IR/aux/graph_coloring.c @@ -1,7 +1,6 @@ #include #include #include -#include #include "array.h" #include "bpf_ir.h" #include "code_gen.h" @@ -28,8 +27,8 @@ void graph_coloring(struct ir_function *fun) { struct ir_insn_cg_extra *extra = insn_cg(insn); struct ir_insn **pos2; - int used_reg[__MAX_BPF_REG] = {0}; - struct array used_spill = INIT_ARRAY(size_t); + int used_reg[MAX_BPF_REG] = {0}; + struct array used_spill = INIT_ARRAY(size_t); array_for(pos2, extra->adj) { struct ir_insn *insn2 = *pos2; // Adj instruction struct ir_insn_cg_extra *extra2 = insn_cg(insn2); @@ -42,7 +41,7 @@ void graph_coloring(struct ir_function *fun) { } } __u8 need_spill = 1; - for (__u8 i = 0; i < __MAX_BPF_REG; i++) { + for (__u8 i = 0; i < MAX_BPF_REG; i++) { if (!used_reg[i]) { extra->allocated = 1; printf("Allocate r%u for %zu\n", i, insn->_insn_id); diff --git a/IR/aux/live_variable.c b/IR/aux/live_variable.c index e6cc6b4a..d69968d5 100644 --- a/IR/aux/live_variable.c +++ b/IR/aux/live_variable.c @@ -5,6 +5,7 @@ #include "bpf_ir.h" #include "code_gen.h" #include "dbg.h" +#include "ir_bb.h" #include "ir_fun.h" #include "ir_insn.h" #include "list.h" @@ -25,26 +26,27 @@ void gen_kill(struct ir_function *fun) { struct ir_basic_block **pos; // For each BB array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_bb_cg_extra *bb_cg = bb->user_data; + struct ir_basic_block *bb = *pos; struct ir_insn *pos2; - // For each operation in reverse - list_for_each_entry_reverse(pos2, &bb->ir_insn_head, list_ptr) { - struct ir_insn *insn_dst = dst(pos2); + // For each operation + list_for_each_entry(pos2, &bb->ir_insn_head, list_ptr) { + struct ir_insn *insn_dst = dst(pos2); + struct ir_insn_cg_extra *insn_cg = pos2->user_data; if (!is_void(pos2) && insn_dst) { - array_erase_elem(&bb_cg->gen, insn_dst); - array_push_unique(&bb_cg->kill, &insn_dst); + array_push_unique(&insn_cg->kill, &insn_dst); } struct array value_uses = get_operands(pos2); struct ir_value **pos3; array_for(pos3, value_uses) { struct ir_value *val = *pos3; if (val->type == IR_VALUE_INSN) { - struct ir_insn *insn = dst(val->data.insn_d); - array_push_unique(&bb_cg->gen, &insn); - array_erase_elem(&bb_cg->kill, insn); + struct ir_insn *insn = val->data.insn_d; + DBGASSERT(insn == dst(insn)); + array_push_unique(&insn_cg->gen, &insn); + // array_erase_elem(&insn_cg->kill, insn); } } + array_free(&value_uses); } } } @@ -100,52 +102,71 @@ void in_out(struct ir_function *fun) { change = 0; struct ir_basic_block **pos; array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_bb_cg_extra *bb_cg = bb->user_data; - struct array old_in = bb_cg->in; - struct ir_basic_block **pos2; - array_clear(&bb_cg->out); - array_for(pos2, bb->succs) { - struct ir_bb_cg_extra *bb_cg2 = (*pos2)->user_data; - merge_array(&bb_cg->out, &bb_cg2->in); - } - struct array out_kill_delta = array_delta(&bb_cg->out, &bb_cg->kill); - bb_cg->in = array_clone(&bb_cg->gen); - merge_array(&bb_cg->in, &out_kill_delta); - // Check for change - if (!equal_set(&bb_cg->in, &old_in)) { - change = 1; + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_insn_cg_extra *insn_cg = insn->user_data; + struct array old_in = insn_cg->in; + array_clear(&insn_cg->out); + + if (get_last_insn(bb) == insn) { + // Last instruction + struct ir_basic_block **pos2; + array_for(pos2, bb->succs) { + struct ir_basic_block *bb2 = *pos2; + if (bb_empty(bb2)) { + CRITICAL("Found empty BB"); + } + struct ir_insn *first = get_first_insn(bb2); + struct ir_insn_cg_extra *insn2_cg = first->user_data; + merge_array(&insn_cg->out, &insn2_cg->in); + } + } else { + // Not last instruction + struct ir_insn *next_insn = + list_entry(insn->list_ptr.next, struct ir_insn, list_ptr); + struct ir_insn_cg_extra *next_insn_cg = next_insn->user_data; + merge_array(&insn_cg->out, &next_insn_cg->in); + } + struct array out_kill_delta = array_delta(&insn_cg->out, &insn_cg->kill); + insn_cg->in = array_clone(&insn_cg->gen); + merge_array(&insn_cg->in, &out_kill_delta); + // Check for change + if (!equal_set(&insn_cg->in, &old_in)) { + change = 1; + } + // Collect grabage + array_free(&out_kill_delta); + array_free(&old_in); } - // Collect grabage - array_free(&out_kill_delta); - array_free(&old_in); } } } -void print_bb_extra(struct ir_basic_block *bb) { - struct ir_bb_cg_extra *bb_cg = bb->user_data; - if (bb->user_data == NULL) { +void print_insn_extra(struct ir_insn *insn) { + struct ir_insn_cg_extra *insn_cg = insn->user_data; + if (insn_cg == NULL) { CRITICAL("NULL user data"); } printf("--\nGen:"); struct ir_insn **pos; - array_for(pos, bb_cg->gen) { + array_for(pos, insn_cg->gen) { struct ir_insn *insn = *pos; printf(" %%%zu", insn->_insn_id); } printf("\nKill:"); - array_for(pos, bb_cg->kill) { + array_for(pos, insn_cg->kill) { struct ir_insn *insn = *pos; printf(" %%%zu", insn->_insn_id); } printf("\nIn:"); - array_for(pos, bb_cg->in) { + array_for(pos, insn_cg->in) { struct ir_insn *insn = *pos; printf(" %%%zu", insn->_insn_id); } printf("\nOut:"); - array_for(pos, bb_cg->out) { + array_for(pos, insn_cg->out) { struct ir_insn *insn = *pos; printf(" %%%zu", insn->_insn_id); } @@ -157,6 +178,6 @@ void liveness_analysis(struct ir_function *fun) { gen_kill(fun); in_out(fun); printf("--------------\n"); - print_ir_prog_advanced(fun, print_bb_extra, print_ir_dst); - print_ir_prog_advanced(fun, NULL, print_ir_dst); + print_ir_prog_advanced(fun, NULL, print_insn_extra, print_ir_dst); + print_ir_prog_advanced(fun, NULL, NULL, print_ir_dst); } diff --git a/IR/aux/prog_check.c b/IR/aux/prog_check.c index 87925ede..02ae3e67 100644 --- a/IR/aux/prog_check.c +++ b/IR/aux/prog_check.c @@ -2,8 +2,81 @@ #include "array.h" #include "bpf_ir.h" #include "dbg.h" +#include "ir_insn.h" #include "list.h" +void check_insn_users_use_insn(struct ir_insn *insn) { + struct ir_insn **pos; + array_for(pos, insn->users) { + struct ir_insn *user = *pos; + // Check if the user actually uses this instruction + struct array operands = get_operands(user); + struct ir_value **val; + int found = 0; + array_for(val, operands) { + struct ir_value *v = *val; + if (v->type == IR_VALUE_INSN && v->data.insn_d == insn) { + // Found the user + found = 1; + break; + } + } + array_free(&operands); + if (!found) { + // Error! + CRITICAL("User does not use the instruction"); + } + } +} + +void check_insn_operand(struct ir_insn *insn) { + struct array operands = get_operands(insn); + struct ir_value **val; + array_for(val, operands) { + struct ir_value *v = *val; + if (v->type == IR_VALUE_INSN) { + // Check if the operand actually is used by this instruction + struct ir_insn **pos2; + int found = 0; + array_for(pos2, v->data.insn_d->users) { + struct ir_insn *user = *pos2; + if (user == insn) { + // Found the user + found = 1; + break; + } + } + if (!found) { + // Error! + CRITICAL("Operand is not used by the instruction"); + } + } + } + array_free(&operands); +} + +// Check if the users are correct (only applicable to SSA IR form) +void check_users(struct ir_function *fun) { + // Check FunctionCallArgument Instructions + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + struct ir_insn *insn = fun->function_arg[i]; + check_insn_users_use_insn(insn); + } + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + // Check users of this instruction + check_insn_users_use_insn(insn); + // Check operands of this instruction + check_insn_operand(insn); + } + } +} + +void check_jumping(struct ir_function *fun) {} + // Check if the PHI nodes are at the beginning of the BB void check_phi(struct ir_function *fun) { struct ir_basic_block **pos; diff --git a/IR/aux/spill.c b/IR/aux/spill.c new file mode 100644 index 00000000..0b7ff28f --- /dev/null +++ b/IR/aux/spill.c @@ -0,0 +1,50 @@ +#include "bpf_ir.h" +#include "code_gen.h" +#include "dbg.h" +enum val_type { + REG, + CONST, + STACK +}; + +enum val_type vtype(struct ir_value val) { + if (val.type == IR_VALUE_INSN) { + struct ir_insn *insn = dst(val.data.insn_d); + struct ir_insn_cg_extra *extra = insn_cg(insn); + if (extra->spilled) { + return STACK; + } else { + return REG; + } + } else { + } +} + +int check_need_spill(struct ir_function *fun) { + // Check if all instruction values are OK for translating + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + if (insn->op == IR_INSN_ALLOC) { + // dst = alloc + // Nothing to do + } else if (insn->op == IR_INSN_STORE) { + // store v0 v1 + // v0: reg + v1: reg, const, stack + // v0: stack + v1: reg, const + + } else if (insn->op == IR_INSN_ASSIGN) { + // dst = + // MOV dst val + + } else { + CRITICAL("No such instruction"); + } + } + } + return 0; +} + +void spill(struct ir_function *fun) {} \ No newline at end of file diff --git a/IR/aux/translate.c b/IR/aux/translate.c new file mode 100644 index 00000000..82e6a206 --- /dev/null +++ b/IR/aux/translate.c @@ -0,0 +1,29 @@ +#include "bpf_ir.h" +#include "code_gen.h" +#include "dbg.h" + +#define IU_REG0 BPF_REG_0 +#define IU_REG1 BPF_REG_1 + +void translate(struct ir_function *fun) { + // fun is still in IR form + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_insn_cg_extra *extra = insn_cg(insn); + + if (insn->op == IR_INSN_ALLOC) { + // dst = alloc + // Nothing to do + } else if (insn->op == IR_INSN_ASSIGN) { + // dst = + // MOV dst val + + } else { + CRITICAL("No such instruction"); + } + } + } +} \ No newline at end of file diff --git a/IR/bpf_ir.c b/IR/bpf_ir.c index 46010eec..39ef5b11 100644 --- a/IR/bpf_ir.c +++ b/IR/bpf_ir.c @@ -12,6 +12,7 @@ #include "list.h" #include "dbg.h" #include "passes.h" +#include "prog_check.h" #include "reachable_bb.h" #include "read.h" @@ -267,16 +268,24 @@ void print_pre_ir_cfg(struct pre_ir_basic_block *bb) { struct ssa_transform_env init_env(struct bb_info info) { struct ssa_transform_env env; for (size_t i = 0; i < MAX_BPF_REG; ++i) { - env.currentDef[i] = array_init(sizeof(struct bb_val)); + env.currentDef[i] = INIT_ARRAY(struct bb_val); } env.info = info; - env.sp_users = array_init(sizeof(struct ir_insn *)); + env.sp_users = INIT_ARRAY(struct ir_insn *); // Initialize function argument - // TODO: more than one arg - struct ir_value val; - val.type = IR_VALUE_FUNCTIONARG; - val.data.arg_id = 0; - write_variable(&env, BPF_REG_1, info.entry, val); + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + env.function_arg[i] = __malloc(sizeof(struct ir_insn)); + + env.function_arg[i]->users = INIT_ARRAY(struct ir_insn *); + env.function_arg[i]->op = IR_INSN_FUNCTIONARG; + env.function_arg[i]->fid = i; + env.function_arg[i]->value_num = 0; + env.function_arg[i]->user_data = NULL; + struct ir_value val; + val.type = IR_VALUE_INSN; + val.data.insn_d = env.function_arg[i]; + write_variable(&env, BPF_REG_1 + i, info.entry, val); + } return env; } @@ -802,13 +811,23 @@ void free_function(struct ir_function *fun) { } free(bb); } + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + array_free(&fun->function_arg[i]->users); + __free(fun->function_arg[i]); + } + array_free(&fun->all_bbs); + array_free(&fun->reachable_bbs); + array_free(&fun->cg_info.all_var); } struct ir_function gen_function(struct ssa_transform_env *env) { struct ir_function fun; - fun.arg_num = 1; - fun.entry = env->info.entry->ir_bb; - fun.sp_users = env->sp_users; + fun.arg_num = 1; + fun.entry = env->info.entry->ir_bb; + fun.sp_users = env->sp_users; + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + fun.function_arg[i] = env->function_arg[i]; + } fun.all_bbs = INIT_ARRAY(struct ir_basic_block *); fun.reachable_bbs = INIT_ARRAY(struct ir_basic_block *); fun.cg_info.all_var = INIT_ARRAY(struct ir_insn *); @@ -835,9 +854,6 @@ __u8 ir_value_equal(struct ir_value a, struct ir_value b) { if (a.type == IR_VALUE_CONSTANT) { return a.data.constant_d.data.s32_d == b.data.constant_d.data.s32_d; } - if (a.type == IR_VALUE_FUNCTIONARG) { - return a.data.arg_id == b.data.arg_id; - } if (a.type == IR_VALUE_INSN) { return a.data.insn_d == b.data.insn_d; } @@ -853,6 +869,8 @@ void run_passes(struct ir_function *fun) { gen_reachable_bbs(fun); passes[i](fun); printf("--------------------\n"); + // Validate the IR + check_users(fun); print_ir_prog(fun); } } diff --git a/IR/include/bpf_ir.h b/IR/include/bpf_ir.h index fdcee64c..462144e3 100644 --- a/IR/include/bpf_ir.h +++ b/IR/include/bpf_ir.h @@ -49,14 +49,13 @@ struct ir_constant { enum ir_value_type { IR_VALUE_CONSTANT, - IR_VALUE_FUNCTIONARG, IR_VALUE_INSN, IR_VALUE_STACK_PTR, IR_VALUE_UNDEF, }; /** - VALUE = CONSTANT | INSN | FUNCTIONARG + VALUE = CONSTANT | INSN "r1 = constant" pattern will use `CONSTANT` which will not be added to BB. */ @@ -64,7 +63,6 @@ struct ir_value { union { struct ir_constant constant_d; struct ir_insn *insn_d; - __u8 arg_id; } data; enum ir_value_type type; }; @@ -109,6 +107,7 @@ enum ir_insn_type { IR_INSN_LOAD, IR_INSN_STORERAW, IR_INSN_LOADRAW, + IR_INSN_FUNCTIONARG, // The function argument store // ALU IR_INSN_ADD, IR_INSN_SUB, @@ -129,7 +128,8 @@ enum ir_insn_type { // PHI IR_INSN_PHI, // Code-gen instructions - IR_INSN_ASSIGN + IR_INSN_ASSIGN, + IR_INSN_REG, }; /** @@ -139,6 +139,8 @@ enum ir_insn_type { | LOAD | STORERAW , | LOADRAW + | FUNCTIONARG + | ADD , | SUB , | MUL , @@ -156,6 +158,7 @@ enum ir_insn_type { | PHI (For code gen usage) | ASSIGN + | REG Note. must be the next basic block. */ @@ -176,8 +179,7 @@ struct ir_insn { // Array of phi_value struct array phi; - __s32 fid; - // __u32 f_arg_num; + __s32 fid; enum ir_insn_type op; // Linked list @@ -190,10 +192,6 @@ struct ir_insn { // Users struct array users; - // Might be useful? - // Too difficult, need BTF - // enum ir_vr_type type; - // Used when generating the real code size_t _insn_id; void *user_data; @@ -288,6 +286,13 @@ struct ssa_transform_env { // Stack pointer (r10) users struct array sp_users; + // Function argument + struct ir_insn *function_arg[MAX_FUNC_ARG]; +}; + +struct error { + __u8 is_kernel_err : 1; + unsigned int errorno : 31; }; // helper functions diff --git a/IR/include/code_gen.h b/IR/include/code_gen.h index 34d478ab..af03a544 100644 --- a/IR/include/code_gen.h +++ b/IR/include/code_gen.h @@ -9,20 +9,25 @@ void code_gen(struct ir_function *fun); // Extra information needed for code gen struct ir_bb_cg_extra { // Liveness analysis - struct array in; - struct array out; - struct array gen; - struct array kill; }; struct ir_insn_cg_extra { // Destination (Not in SSA form anymore) struct ir_insn *dst; + // Liveness analysis + struct array in; + struct array out; + struct array gen; + struct array kill; + // Adj list in interference graph // Array of struct ir_insn* struct array adj; + // Translated pre_ir_insn: array of struct pre_ir_insn + struct array translated; + __u8 allocated; // When allocating register, whether dst will be spilled @@ -38,6 +43,8 @@ struct ir_insn_cg_extra { struct ir_insn_cg_extra *insn_cg(struct ir_insn *insn); +struct ir_insn_cg_extra *init_insn_cg(struct ir_insn *insn); + struct ir_insn *dst(struct ir_insn *insn); void to_cssa(struct ir_function *fun); @@ -54,4 +61,6 @@ void print_interference_graph(struct ir_function *fun); void graph_coloring(struct ir_function *fun); +void explicit_reg(struct ir_function *fun); + #endif diff --git a/IR/include/dbg.h b/IR/include/dbg.h index e38d1999..ecd5a83f 100644 --- a/IR/include/dbg.h +++ b/IR/include/dbg.h @@ -3,6 +3,7 @@ #include #include +#include #define CRITICAL(str) \ { \ @@ -10,4 +11,6 @@ exit(1); \ } +#define DBGASSERT(cond) assert(cond) + #endif diff --git a/IR/include/ir_bb.h b/IR/include/ir_bb.h index d526a73f..e6cf6bc1 100644 --- a/IR/include/ir_bb.h +++ b/IR/include/ir_bb.h @@ -18,4 +18,8 @@ struct ir_basic_block *split_bb(struct ir_function *fun, struct ir_insn *insn); struct ir_insn *get_last_insn(struct ir_basic_block *bb); +struct ir_insn *get_first_insn(struct ir_basic_block *bb); + +int bb_empty(struct ir_basic_block *bb); + #endif diff --git a/IR/include/ir_fun.h b/IR/include/ir_fun.h index 9ade35de..ccb0983d 100644 --- a/IR/include/ir_fun.h +++ b/IR/include/ir_fun.h @@ -7,6 +7,9 @@ struct code_gen_info { // All vertex in interference graph // Array of struct ir_insn* struct array all_var; + + // BPF Register Virtual Instruction (used as dst) + struct ir_insn *regs[MAX_BPF_REG]; }; struct ir_function { @@ -24,6 +27,9 @@ struct ir_function { // Stack pointer (r10) users. Should be readonly. No more manual stack access should be allowed. struct array sp_users; + // Function argument + struct ir_insn *function_arg[MAX_FUNC_ARG]; + // Array of struct ir_constraint. Value constraints. struct array value_constraints; diff --git a/IR/include/ir_helper.h b/IR/include/ir_helper.h index 5e7b435c..a91cf6e4 100644 --- a/IR/include/ir_helper.h +++ b/IR/include/ir_helper.h @@ -7,8 +7,8 @@ void clean_env_all(struct ir_function *fun); void print_ir_prog(struct ir_function *); -void print_ir_prog_advanced(struct ir_function *fun, void (*post_fun)(struct ir_basic_block *), - void (*print_ir)(struct ir_insn *)); +void print_ir_prog_advanced(struct ir_function *, void (*)(struct ir_basic_block *), + void (*)(struct ir_insn *), void (*)(struct ir_insn *)); void print_ir_dst(struct ir_insn *insn); diff --git a/IR/include/ir_insn.h b/IR/include/ir_insn.h index d1924f1b..21eea069 100644 --- a/IR/include/ir_insn.h +++ b/IR/include/ir_insn.h @@ -16,6 +16,8 @@ struct array get_operands(struct ir_insn *insn); void replace_all_usage(struct ir_insn *insn, struct ir_value rep); +void replace_all_usage_except(struct ir_insn *insn, struct ir_value rep, struct ir_insn *except); + void erase_insn(struct ir_insn *insn); int is_void(struct ir_insn *insn); @@ -24,6 +26,8 @@ int is_jmp(struct ir_insn *insn); struct ir_insn *prev_insn(struct ir_insn *insn); +struct ir_insn *next_insn(struct ir_insn *insn); + struct ir_insn *create_alloc_insn(struct ir_insn *insn, enum ir_vr_type type, enum insert_position pos); @@ -86,4 +90,12 @@ void val_add_user(struct ir_value val, struct ir_insn *user); void val_remove_user(struct ir_value val, struct ir_insn *user); +struct ir_insn *create_assign_insn_cg(struct ir_insn *insn, struct ir_value val, + enum insert_position pos); + +struct ir_insn *create_assign_insn_bb_cg(struct ir_basic_block *bb, struct ir_value val, + enum insert_position pos); + +void replace_operand(struct ir_insn *insn, struct ir_value v1, struct ir_value v2); + #endif diff --git a/IR/include/prog_check.h b/IR/include/prog_check.h index 07fe631e..aa54d498 100644 --- a/IR/include/prog_check.h +++ b/IR/include/prog_check.h @@ -5,4 +5,6 @@ void prog_check(struct ir_function *fun); +void check_users(struct ir_function *fun); + #endif diff --git a/IR/ir_bb.c b/IR/ir_bb.c index f1d4615f..93ca2c4c 100644 --- a/IR/ir_bb.c +++ b/IR/ir_bb.c @@ -11,6 +11,10 @@ size_t bb_len(struct ir_basic_block *bb) { return len; } +int bb_empty(struct ir_basic_block *bb) { + return list_empty(&bb->ir_insn_head); +} + struct ir_basic_block *create_bb(struct ir_function *fun) { struct ir_basic_block *new_bb = init_ir_bb_raw(); array_push(&fun->all_bbs, &new_bb); @@ -56,8 +60,15 @@ struct ir_basic_block *split_bb(struct ir_function *fun, struct ir_insn *insn) { } struct ir_insn *get_last_insn(struct ir_basic_block *bb) { - if (list_empty(&bb->ir_insn_head)) { + if (bb_empty(bb)) { return NULL; } return list_entry(bb->ir_insn_head.prev, struct ir_insn, list_ptr); } + +struct ir_insn *get_first_insn(struct ir_basic_block *bb) { + if (bb_empty(bb)) { + return NULL; + } + return list_entry(bb->ir_insn_head.next, struct ir_insn, list_ptr); +} diff --git a/IR/ir_code_gen.c b/IR/ir_code_gen.c index d0fda16f..542520b0 100644 --- a/IR/ir_code_gen.c +++ b/IR/ir_code_gen.c @@ -3,38 +3,69 @@ #include "bpf_ir.h" #include "code_gen.h" #include "dbg.h" +#include "ir_insn.h" #include "list.h" #include "prog_check.h" #include "ir_helper.h" +struct ir_insn_cg_extra *init_insn_cg(struct ir_insn *insn) { + struct ir_insn_cg_extra *extra = __malloc(sizeof(struct ir_insn_cg_extra)); + // When init, the destination is itself + if (is_void(insn)) { + extra->dst = NULL; + } else { + extra->dst = insn; + } + extra->adj = INIT_ARRAY(struct ir_insn *); + extra->allocated = 0; + extra->spilled = 0; + extra->alloc_reg = 0; + extra->translated = INIT_ARRAY(struct pre_ir_insn); + extra->gen = INIT_ARRAY(struct ir_insn *); + extra->kill = INIT_ARRAY(struct ir_insn *); + extra->in = INIT_ARRAY(struct ir_insn *); + extra->out = INIT_ARRAY(struct ir_insn *); + insn->user_data = extra; + return extra; +} + void init_cg(struct ir_function *fun) { struct ir_basic_block **pos; array_for(pos, fun->reachable_bbs) { struct ir_basic_block *bb = *pos; struct ir_bb_cg_extra *bb_cg = __malloc(sizeof(struct ir_bb_cg_extra)); - bb_cg->gen = INIT_ARRAY(struct ir_insn *); - bb_cg->kill = INIT_ARRAY(struct ir_insn *); - bb_cg->in = INIT_ARRAY(struct ir_insn *); - bb_cg->out = INIT_ARRAY(struct ir_insn *); - + // Empty bb cg bb->user_data = bb_cg; struct ir_insn *insn; list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - struct ir_insn_cg_extra *insn_cg = __malloc(sizeof(struct ir_insn_cg_extra)); - // When init, the destination is itself - if (insn->users.num_elem > 0) { - insn_cg->dst = insn; - } else { - insn_cg->dst = NULL; - } - insn_cg->adj = INIT_ARRAY(struct ir_insn *); - insn_cg->allocated = 0; - insn_cg->spilled = 0; - insn_cg->alloc_reg = 0; - insn->user_data = insn_cg; + init_insn_cg(insn); } } + + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + fun->cg_info.regs[i] = __malloc(sizeof(struct ir_insn)); + struct ir_insn *insn = fun->cg_info.regs[i]; + insn->op = IR_INSN_REG; + insn->parent_bb = NULL; + insn->users = INIT_ARRAY(struct ir_insn *); + insn->value_num = 0; + struct ir_insn_cg_extra *extra = init_insn_cg(insn); + extra->alloc_reg = i; + extra->dst = insn; + } +} + +void free_insn_cg(struct ir_insn *insn) { + struct ir_insn_cg_extra *extra = insn_cg(insn); + array_free(&extra->adj); + array_free(&extra->translated); + array_free(&extra->gen); + array_free(&extra->kill); + array_free(&extra->in); + array_free(&extra->out); + __free(extra); + insn->user_data = NULL; } void free_cg_res(struct ir_function *fun) { @@ -42,20 +73,20 @@ void free_cg_res(struct ir_function *fun) { array_for(pos, fun->reachable_bbs) { struct ir_basic_block *bb = *pos; struct ir_bb_cg_extra *bb_cg = bb->user_data; - array_free(&bb_cg->gen); - array_free(&bb_cg->kill); - array_free(&bb_cg->in); - array_free(&bb_cg->out); - __free(bb->user_data); + __free(bb_cg); bb->user_data = NULL; struct ir_insn *insn; list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - struct ir_insn_cg_extra *insn_cg = insn->user_data; - array_free(&insn_cg->adj); - __free(insn_cg); - insn->user_data = NULL; + free_insn_cg(insn); } } + + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + struct ir_insn *insn = fun->cg_info.regs[i]; + array_free(&insn->users); + free_insn_cg(insn); + __free(insn); + } } struct ir_insn_cg_extra *insn_cg(struct ir_insn *insn) { @@ -68,7 +99,7 @@ struct ir_insn *dst(struct ir_insn *insn) { void print_ir_prog_cg(struct ir_function *fun) { printf("-----------------\n"); - print_ir_prog_advanced(fun, NULL, NULL); + print_ir_prog_advanced(fun, NULL, NULL, NULL); } void code_gen(struct ir_function *fun) { @@ -78,26 +109,33 @@ void code_gen(struct ir_function *fun) { prog_check(fun); // Step 2: Eliminate SSA to_cssa(fun); + check_users(fun); print_ir_prog_cg(fun); // Init CG, start real code generation - // No "users" available after this step init_cg(fun); + explicit_reg(fun); // Still in SSA form, users are available + print_ir_prog_cg(fun); + printf("-----------------\n"); + print_ir_prog_advanced(fun, NULL, NULL, print_ir_dst); + + // SSA Destruction + // users not available from now on - remove_phi(fun); + // remove_phi(fun); - // Step 3: Liveness Analysis - liveness_analysis(fun); + // // Step 3: Liveness Analysis + // liveness_analysis(fun); - // Step 4: Conflict Analysis - conflict_analysis(fun); - print_interference_graph(fun); - printf("-------------\n"); + // // Step 4: Conflict Analysis + // conflict_analysis(fun); + // print_interference_graph(fun); + // printf("-------------\n"); - // Step 5: Graph coloring - graph_coloring(fun); - print_interference_graph(fun); - print_ir_prog_advanced(fun, NULL, print_ir_alloc); + // // Step 5: Graph coloring + // graph_coloring(fun); + // print_interference_graph(fun); + // print_ir_prog_advanced(fun, NULL, NULL, print_ir_alloc); // Register allocation finished diff --git a/IR/ir_helper.c b/IR/ir_helper.c index 3a0f9ae4..5171d79d 100644 --- a/IR/ir_helper.c +++ b/IR/ir_helper.c @@ -85,15 +85,27 @@ void print_constant(struct ir_constant d) { } } +void print_insn_ptr_base(struct ir_insn *insn) { + if (insn->op == IR_INSN_REG) { + printf("R%u", insn_cg(insn)->alloc_reg); + return; + } + if (insn->op == IR_INSN_FUNCTIONARG) { + printf("arg%u", insn->fid); + return; + } + if (insn->_insn_id == SIZE_MAX) { + printf("%p", insn); + return; + } + printf("%%%zu", insn->_insn_id); +} + void print_insn_ptr(struct ir_insn *insn, void (*print_ir)(struct ir_insn *)) { if (print_ir) { print_ir(insn); } else { - if (insn->_insn_id == SIZE_MAX) { - printf("%p", insn); - return; - } - printf("%%%zu", insn->_insn_id); + print_insn_ptr_base(insn); } } @@ -116,9 +128,6 @@ void print_ir_value_full(struct ir_value v, void (*print_ir)(struct ir_insn *)) case IR_VALUE_CONSTANT: print_constant(v.data.constant_d); break; - case IR_VALUE_FUNCTIONARG: - printf("arg%d", v.data.arg_id); - break; case IR_VALUE_UNDEF: printf("undef"); break; @@ -366,8 +375,8 @@ void print_raw_ir_insn(struct ir_insn *insn) { print_raw_ir_insn_full(insn, 0); } -void print_ir_bb(struct ir_basic_block *bb, void (*post_fun)(struct ir_basic_block *), - void (*print_ir)(struct ir_insn *)) { +void print_ir_bb(struct ir_basic_block *bb, void (*post_bb)(struct ir_basic_block *), + void (*post_insn)(struct ir_insn *), void (*print_insn_name)(struct ir_insn *)) { if (bb->_visited) { return; } @@ -380,23 +389,26 @@ void print_ir_bb(struct ir_basic_block *bb, void (*post_fun)(struct ir_basic_blo printf(" "); } else { printf(" "); - if (print_ir) { - print_ir(insn); + if (print_insn_name) { + print_insn_name(insn); } else { printf("%%%zu", insn->_insn_id); } printf(" = "); } - print_ir_insn_full(insn, print_ir); + print_ir_insn_full(insn, print_insn_name); printf("\n"); + if (post_insn) { + post_insn(insn); + } } - if (post_fun) { - post_fun(bb); + if (post_bb) { + post_bb(bb); } for (size_t i = 0; i < bb->succs.num_elem; ++i) { struct ir_basic_block *next = ((struct ir_basic_block **)(bb->succs.data))[i]; - print_ir_bb(next, post_fun, print_ir); + print_ir_bb(next, post_bb, post_insn, print_insn_name); } } @@ -443,18 +455,14 @@ void tag_ir(struct ir_function *fun) { void print_ir_prog(struct ir_function *fun) { tag_ir(fun); - print_ir_bb(fun->entry, NULL, 0); + print_ir_bb(fun->entry, NULL, NULL, NULL); clean_tag(fun); } void print_ir_dst(struct ir_insn *insn) { insn = dst(insn); if (insn) { - if (insn->_insn_id == SIZE_MAX) { - printf("%p", insn); - return; - } - printf("%%%zu", insn->_insn_id); + print_insn_ptr_base(insn); } else { printf("(NULL)"); } @@ -478,9 +486,10 @@ void print_ir_alloc(struct ir_insn *insn) { } } -void print_ir_prog_advanced(struct ir_function *fun, void (*post_fun)(struct ir_basic_block *), - void (*print_ir)(struct ir_insn *)) { +void print_ir_prog_advanced(struct ir_function *fun, void (*post_bb)(struct ir_basic_block *), + void (*post_insn)(struct ir_insn *), + void (*print_insn_name)(struct ir_insn *)) { tag_ir(fun); - print_ir_bb(fun->entry, post_fun, print_ir); + print_ir_bb(fun->entry, post_bb, post_insn, print_insn_name); clean_tag(fun); } diff --git a/IR/ir_insn.c b/IR/ir_insn.c index 7de70807..32df4cf0 100644 --- a/IR/ir_insn.c +++ b/IR/ir_insn.c @@ -2,9 +2,11 @@ #include #include "array.h" #include "bpf_ir.h" +#include "code_gen.h" #include "dbg.h" #include "ir_bb.h" #include "list.h" +#include "ir_helper.h" struct ir_insn *create_insn_base(struct ir_basic_block *bb) { struct ir_insn *new_insn = __malloc(sizeof(struct ir_insn)); @@ -14,9 +16,29 @@ struct ir_insn *create_insn_base(struct ir_basic_block *bb) { return new_insn; } +struct ir_insn *create_insn_base_cg(struct ir_basic_block *bb) { + struct ir_insn *new_insn = create_insn_base(bb); + init_insn_cg(new_insn); + insn_cg(new_insn)->dst = new_insn; + return new_insn; +} + +void replace_operand(struct ir_insn *insn, struct ir_value v1, struct ir_value v2) { + // Replace v1 with v2 in insn + if (v1.type == IR_VALUE_INSN) { + // Remove user from v1 + val_remove_user(v1, insn); + } + if (v2.type == IR_VALUE_INSN) { + val_add_user(v2, insn); + } +} + void replace_all_usage(struct ir_insn *insn, struct ir_value rep) { struct ir_insn **pos; - array_for(pos, insn->users) { + struct array users = insn->users; + insn->users = INIT_ARRAY(struct ir_insn *); + array_for(pos, users) { struct ir_insn *user = *pos; struct array operands = get_operands(user); struct ir_value **pos2; @@ -24,9 +46,36 @@ void replace_all_usage(struct ir_insn *insn, struct ir_value rep) { if ((*pos2)->type == IR_VALUE_INSN && (*pos2)->data.insn_d == insn) { // Match, replace **pos2 = rep; + val_add_user(rep, user); + } + } + array_free(&operands); + } + array_free(&users); +} + +void replace_all_usage_except(struct ir_insn *insn, struct ir_value rep, struct ir_insn *except) { + struct ir_insn **pos; + struct array users = insn->users; + insn->users = INIT_ARRAY(struct ir_insn *); + array_for(pos, users) { + struct ir_insn *user = *pos; + if (user == except) { + array_push(&insn->users, &user); + continue; + } + struct array operands = get_operands(user); + struct ir_value **pos2; + array_for(pos2, operands) { + if ((*pos2)->type == IR_VALUE_INSN && (*pos2)->data.insn_d == insn) { + // Match, replace + **pos2 = rep; + val_add_user(rep, user); } } + array_free(&operands); } + array_free(&users); } struct array get_operands(struct ir_insn *insn) { @@ -52,6 +101,13 @@ __u8 is_last_insn(struct ir_insn *insn) { } void erase_insn(struct ir_insn *insn) { + // TODO: remove users + struct array operands = get_operands(insn); + struct ir_value **pos2; + array_for(pos2, operands) { + val_remove_user((**pos2), insn); + } + array_free(&operands); list_del(&insn->list_ptr); __free(insn); } @@ -228,12 +284,20 @@ struct ir_insn *create_bin_insn_bb(struct ir_basic_block *bb, struct ir_value va struct ir_insn *prev_insn(struct ir_insn *insn) { struct list_head *prev = insn->list_ptr.prev; - if (list_empty(prev)) { + if (prev == &insn->parent_bb->ir_insn_head) { return NULL; } return list_entry(prev, struct ir_insn, list_ptr); } +struct ir_insn *next_insn(struct ir_insn *insn) { + struct list_head *next = insn->list_ptr.next; + if (next == &insn->parent_bb->ir_insn_head) { + return NULL; + } + return list_entry(next, struct ir_insn, list_ptr); +} + struct ir_insn *create_ja_insn_base(struct ir_basic_block *bb, struct ir_basic_block *to_bb) { struct ir_insn *new_insn = create_insn_base(bb); new_insn->op = IR_INSN_JA; @@ -345,6 +409,29 @@ struct ir_insn *create_assign_insn_bb(struct ir_basic_block *bb, struct ir_value return new_insn; } +struct ir_insn *create_assign_insn_base_cg(struct ir_basic_block *bb, struct ir_value val) { + struct ir_insn *new_insn = create_insn_base_cg(bb); + new_insn->op = IR_INSN_ASSIGN; + new_insn->values[0] = val; + new_insn->value_num = 1; + val_add_user(val, new_insn); + return new_insn; +} + +struct ir_insn *create_assign_insn_cg(struct ir_insn *insn, struct ir_value val, + enum insert_position pos) { + struct ir_insn *new_insn = create_assign_insn_base_cg(insn->parent_bb, val); + insert_at(new_insn, insn, pos); + return new_insn; +} + +struct ir_insn *create_assign_insn_bb_cg(struct ir_basic_block *bb, struct ir_value val, + enum insert_position pos) { + struct ir_insn *new_insn = create_assign_insn_base_cg(bb, val); + insert_at_bb(new_insn, bb, pos); + return new_insn; +} + struct ir_insn *create_phi_insn_base(struct ir_basic_block *bb) { struct ir_insn *new_insn = create_insn_base(bb); new_insn->op = IR_INSN_PHI; diff --git a/IR/passes/phi_pass.c b/IR/passes/phi_pass.c index e13bb674..b5f1eaf6 100644 --- a/IR/passes/phi_pass.c +++ b/IR/passes/phi_pass.c @@ -2,6 +2,7 @@ #include #include "array.h" #include "bpf_ir.h" +#include "dbg.h" #include "ir_insn.h" #include "list.h" @@ -31,29 +32,13 @@ void try_remove_trivial_phi(struct ir_insn *phi) { if (!same_has_value) { same.type = IR_VALUE_UNDEF; } - struct ir_value phi_val; - phi_val.type = IR_VALUE_INSN; - phi_val.data.insn_d = phi; - struct ir_insn **pos; - array_for(pos, phi->users) { - struct ir_insn *user = *pos; - if (user == phi) { - continue; - } + replace_all_usage_except(phi, same, phi); - struct array value_uses = get_operands(user); - struct ir_value **pos2; - array_for(pos2, value_uses) { - if (ir_value_equal(**pos2, phi_val)) { - **pos2 = same; - } - } - array_free(&value_uses); - } erase_insn(phi); } void remove_trivial_phi(struct ir_function *fun) { + printf("PHI removal\n"); for (size_t i = 0; i < fun->reachable_bbs.num_elem; ++i) { struct ir_basic_block *bb = ((struct ir_basic_block **)(fun->reachable_bbs.data))[i]; struct ir_insn *pos, *n; diff --git a/IR/tests/loop1.c b/IR/tests/loop1.c index b0a0d0a1..a2cbe6e6 100644 --- a/IR/tests/loop1.c +++ b/IR/tests/loop1.c @@ -8,8 +8,10 @@ SEC("xdp") int prog(void *ctx) { __u64 t = bpf_ktime_get_ns(); - for (int i = 0; i < t; ++i) { - bpf_trace_printk("s", 1); + bpf_trace_printk(ctx, t); + for (__u64 i = 0; i < t; ++i) { + bpf_trace_printk("s", i); + bpf_trace_printk(ctx, 2); } return 0; }