void test_instruction_positions_are_computed_in_basic_block_order(void) { struct compilation_unit *cu = compilation_unit_alloc(&method); struct basic_block *b1, *b2; struct insn *insns[4]; unsigned long i; for (i = 0; i < ARRAY_SIZE(insns); i++) insns[i] = alloc_insn(INSN_ADD); b1 = get_basic_block(cu, 0, ARRAY_SIZE(insns)/2); for (i = 0 ; i < ARRAY_SIZE(insns) / 2; i++) bb_add_insn(b1, insns[i]); b2 = get_basic_block(cu, ARRAY_SIZE(insns) / 2, ARRAY_SIZE(insns)); for (i = ARRAY_SIZE(insns) / 2 ; i < ARRAY_SIZE(insns); i++) bb_add_insn(b2, insns[i]); compute_insn_positions(cu); for (i = 0; i < ARRAY_SIZE(insns); i++) assert_int_equals(i * 2, insns[i]->lir_pos); free_compilation_unit(cu); }
void test_branch_opcode_ends_basic_block(void) { struct basic_block *bb1, *bb2, *bb3; struct compilation_unit *cu; struct cafebabe_method_info method_info; struct vm_method method = { .code_attribute.code = default_string, .code_attribute.code_length = ARRAY_SIZE(default_string), .method = &method_info, }; cu = compilation_unit_alloc(&method); analyze_control_flow(cu); assert_int_equals(3, nr_bblocks(cu)); bb1 = bb_entry(cu->bb_list.next); bb2 = bb_entry(bb1->bb_list_node.next); bb3 = bb_entry(bb2->bb_list_node.next); assert_basic_block(cu, 0, 4, bb1); assert_basic_block(cu, 4, 7, bb2); assert_basic_block(cu, 7, 9, bb3); assert_basic_block_successors((struct basic_block*[]){bb2, bb3}, 2, bb1); assert_basic_block_successors((struct basic_block*[]){bb3 }, 1, bb2);
void test_convert_tableswitch_bbs1(void) { struct basic_block *bb0, *bb1, *bb2, *bb3, *bb4, *bb5, *bb6; struct compilation_unit *cu; struct cafebabe_method_info method_info; struct vm_method method = { .code_attribute.code = tableswitch1, .code_attribute.code_length = ARRAY_SIZE(tableswitch1), .method = &method_info, }; memset(&method_info, 0, sizeof(method_info)); cu = compilation_unit_alloc(&method); analyze_control_flow(cu); convert_to_ir(cu); assert_int_equals(7, nr_bblocks(cu)); bb0 = bb_entry(cu->bb_list.next); bb1 = bb_entry(bb0->bb_list_node.next); bb2 = bb_entry(bb1->bb_list_node.next); bb3 = bb_entry(bb2->bb_list_node.next); bb4 = bb_entry(bb3->bb_list_node.next); bb5 = bb_entry(bb4->bb_list_node.next); bb6 = bb_entry(bb5->bb_list_node.next); assert_basic_block_successors((struct basic_block*[]){bb6, bb1}, 2, bb0); assert_basic_block_successors((struct basic_block*[]){bb6, bb2}, 2, bb1);
void test_no_basic_block_when_offset_out_of_range(void) { struct compilation_unit *cu = compilation_unit_alloc(&method); struct basic_block *block = alloc_basic_block(cu, 1, 2); list_add_tail(&block->bb_list_node, &cu->bb_list); assert_ptr_equals(NULL, find_bb(cu, 0)); assert_ptr_equals(NULL, find_bb(cu, 2)); free_compilation_unit(cu); }
void test_reload_insn_is_inserted_at_the_beginning_of_the_interval_if_necessary(void) { struct compilation_unit *cu; struct insn *insn_array[2]; struct var_info *r1, *r2; struct basic_block *bb; struct insn *insn; cu = compilation_unit_alloc(&method); r1 = get_var(cu, J_INT); r2 = get_var(cu, J_INT); insn_array[0] = arithmetic_insn(INSN_ADD, r1, r1, r1); insn_array[1] = arithmetic_insn(INSN_ADD, r2, r2, r2); bb = get_basic_block(cu, 0, 2); bb_add_insn(bb, insn_array[0]); bb_add_insn(bb, insn_array[1]); r1->interval->spill_reload_reg.interval = r1->interval; r2->interval->spill_reload_reg.interval = r2->interval; r2->interval->flags |= INTERVAL_FLAG_NEED_RELOAD; r2->interval->spill_parent = r1->interval; compute_insn_positions(cu); analyze_liveness(cu); insert_spill_reload_insns(cu); /* * A reload instruction is inserted at the beginning. */ insn = list_first_entry(&bb->insn_list, struct insn, insn_list_node); assert_ld_insn(INSN_LD_LOCAL, r2->interval->reg, r1->interval->spill_slot, insn); /* * Second instruction stays the same. */ insn = list_next_entry(&insn->insn_list_node, struct insn, insn_list_node); assert_ptr_equals(insn_array[0], insn); /* * Last instruction stays the same. */ insn = list_next_entry(&insn->insn_list_node, struct insn, insn_list_node); assert_ptr_equals(insn_array[1], insn); free_compilation_unit(cu); }
void test_variable_range_spans_two_basic_blocks(void) { struct basic_block *bb1, *bb2; struct compilation_unit *cu; struct var_info *r1, *r2; struct insn *insn[4]; cu = compilation_unit_alloc(&method); r1 = get_var(cu); r2 = get_var(cu); bb1 = get_basic_block(cu, 0, 2); bb2 = get_basic_block(cu, 2, 4); bb_add_successor(bb1, bb2); insn[2] = imm_insn(INSN_SETL, 0x02, r2); bb_add_insn(bb2, insn[2]); insn[3] = arithmetic_insn(INSN_ADD, r1, r2, r2); bb_add_insn(bb2, insn[3]); insn[0] = imm_insn(INSN_SETL, 0x01, r1); bb_add_insn(bb1, insn[0]); insn[1] = branch_insn(INSN_JMP, bb2); bb_add_insn(bb1, insn[1]); compute_insn_positions(cu); analyze_liveness(cu); assert_defines(bb1, r1); assert_defines(bb2, r2); assert_uses(bb2, r1); assert_live_range(r1->interval, 0, 4); assert_live_range(r2->interval, 2, 4); assert_ptr_equals(insn[0], r1->interval->insn_array[0]); assert_ptr_equals(insn[1], r1->interval->insn_array[1]); assert_ptr_equals(insn[2], r1->interval->insn_array[2]); assert_ptr_equals(insn[3], r1->interval->insn_array[3]); assert_ptr_equals(insn[2], r2->interval->insn_array[0]); assert_ptr_equals(insn[3], r2->interval->insn_array[1]); free_compilation_unit(cu); }
void test_spill_insn_is_inserted_at_the_end_of_the_interval_if_necessary(void) { struct compilation_unit *cu; struct insn *insn_array[2]; struct var_info *r1, *r2; struct basic_block *bb; struct insn *insn; cu = compilation_unit_alloc(&method); r1 = get_var(cu); r2 = get_var(cu); insn_array[0] = arithmetic_insn(INSN_ADD, r1, r1, r1); insn_array[1] = arithmetic_insn(INSN_ADD, r2, r2, r2); bb = get_basic_block(cu, 0, 2); bb_add_insn(bb, insn_array[0]); bb_add_insn(bb, insn_array[1]); r1->interval->need_spill = true; compute_insn_positions(cu); analyze_liveness(cu); insert_spill_reload_insns(cu); /* * First instruction stays the same. */ insn = list_first_entry(&bb->insn_list, struct insn, insn_list_node); assert_ptr_equals(insn_array[0], insn); /* * Last instruction stays the same. */ insn = list_next_entry(&insn->insn_list_node, struct insn, insn_list_node); assert_ptr_equals(insn_array[1], insn); /* * A spill instruction is inserted after the interval end. */ insn = list_next_entry(&insn->insn_list_node, struct insn, insn_list_node); assert_st_insn(INSN_ST_LOCAL, r1->interval->spill_slot, r1->interval->reg, insn); free_compilation_unit(cu); }
void test_empty_interval_is_never_spilled(void) { struct compilation_unit *cu; struct basic_block *bb; struct var_info *r1; cu = compilation_unit_alloc(&method); bb = get_basic_block(cu, 0, 2); r1 = get_var(cu); r1->interval->need_spill = true; compute_insn_positions(cu); analyze_liveness(cu); insert_spill_reload_insns(cu); free_compilation_unit(cu); }
void test_find_basic_block(void) { struct basic_block *b1; struct basic_block *b2; struct basic_block *b3; struct compilation_unit *cu = compilation_unit_alloc(&method); b1 = alloc_basic_block(cu, 0, 3); b2 = alloc_basic_block(cu, 3, 5); b3 = alloc_basic_block(cu, 5, 6); list_add_tail(&b1->bb_list_node, &cu->bb_list); list_add_tail(&b2->bb_list_node, &cu->bb_list); list_add_tail(&b3->bb_list_node, &cu->bb_list); assert_ptr_equals(b1, find_bb(cu, 2)); assert_ptr_equals(b2, find_bb(cu, 3)); assert_ptr_equals(b3, find_bb(cu, 5)); free_compilation_unit(cu); }
void test_variable_range_limited_to_basic_block(void) { struct compilation_unit *cu; struct var_info *r1, *r2; struct basic_block *bb; struct insn *insn[3]; cu = compilation_unit_alloc(&method); r1 = get_var(cu); r2 = get_var(cu); bb = get_basic_block(cu, 0, 3); insn[0] = imm_insn(INSN_SETL, 0x01, r1); bb_add_insn(bb, insn[0]); insn[1] = imm_insn(INSN_SETL, 0x02, r2); bb_add_insn(bb, insn[1]); insn[2] = arithmetic_insn(INSN_ADD, r1, r2, r2); bb_add_insn(bb, insn[2]); compute_insn_positions(cu); analyze_liveness(cu); assert_defines(bb, r1); assert_defines(bb, r2); assert_live_range(r1->interval, 0, 3); assert_live_range(r2->interval, 1, 3); assert_ptr_equals(insn[0], r1->interval->insn_array[0]); assert_ptr_equals(insn[1], r1->interval->insn_array[1]); assert_ptr_equals(insn[2], r1->interval->insn_array[2]); assert_ptr_equals(insn[1], r2->interval->insn_array[0]); assert_ptr_equals(insn[2], r2->interval->insn_array[1]); free_compilation_unit(cu); }
void test_honors_fixed_interval_register_constraint_for_overlapping_intervals(void) { struct compilation_unit *cu; struct var_info *v1, *v2; cu = compilation_unit_alloc(&method); v1 = get_fixed_var(cu, R0); v1->interval->range.start = 0; v1->interval->range.end = 2; v2 = get_var(cu); v2->interval->range.start = 0; v2->interval->range.end = 2; allocate_registers(cu); assert_int_equals(R0, v1->interval->reg); assert_int_equals(R1, v2->interval->reg); free_compilation_unit(cu); }
static void assert_emit_target_for_backward_branches(unsigned char expected_prefix, unsigned char expected_opc, enum insn_type insn_type) { struct basic_block *target_bb; struct compilation_unit *cu; struct var_info *eax, *ebx; cu = compilation_unit_alloc(&method); eax = get_fixed_var(cu, MACH_REG_EAX); ebx = get_fixed_var(cu, MACH_REG_EBX); target_bb = get_basic_block(cu, 0, 1); bb_add_insn(target_bb, imm_reg_insn(INSN_ADD_IMM_REG, 0x01, eax)); assert_emits_branch_target(expected_prefix, expected_opc, 0xf8, 0xff, 0xff, 0xff, target_bb, insn_type); bb_add_insn(target_bb, imm_reg_insn(INSN_ADD_IMM_REG, 0x02, ebx)); assert_emits_branch_target(expected_prefix, expected_opc, 0xf5, 0xff, 0xff, 0xff, target_bb, insn_type); free_compilation_unit(cu); }
static void assert_backpatches_unresolved_branches_when_emitting_target( unsigned char expected_prefix, unsigned char expected_opc, enum insn_type insn_type) { struct basic_block *target_bb, *branch_bb; struct compilation_unit *cu; struct var_info *eax; cu = compilation_unit_alloc(&method); eax = get_fixed_var(cu, MACH_REG_EAX); branch_bb = get_basic_block(cu, 0, 1); target_bb = get_basic_block(cu, 1, 2); bb_add_insn(branch_bb, branch_insn(insn_type, target_bb)); assert_backpatches_branches(expected_prefix, expected_opc, 0x00, branch_bb, target_bb); bb_add_insn(branch_bb, imm_reg_insn(INSN_ADD_IMM_REG, 0x01, eax)); assert_backpatches_branches(expected_prefix, expected_opc, 0x03, branch_bb, target_bb); free_compilation_unit(cu); }