def test_convert_jmpthenlabel_check(): code = [ p.LabelStmt("alreadySorted"), p.AssignOp(src="inbox", dst="emp"), p.IfOp("neg", [p.JumpOp("alreadySorted")], []), p.IfOp("ez", [p.JumpOp("alreadySorted")], [p.AssignOp(src="0", dst="emp")]) ] expected_ast = [ p.LabelStmt("alreadySorted"), p.AssignOp(src="inbox", dst="emp"), p.JumpCondOp(condition="jneg", label_name="alreadySorted"), # pass # p.JumpOp("_hrm_endif_1"), # p.LabelStmt("_hrm_endif_1"), p.JumpCondOp(condition="jez", label_name="alreadySorted"), p.AssignOp(src="0", dst="emp") # p.JumpOp("_hrm_unreachable"), # p.LabelStmt("_hrm_unreachable") ] result_ast = conversion.convert_ifnz_to_ifez(code) result_ast = conversion.convert_iftojump(result_ast) result_ast = conversion.compress_jumps(result_ast) result_ast = conversion.remove_unreachable_code(result_ast) ast = conversion.fix_jmp_then_label(result_ast) assert ast == expected_ast
def test_compress_jcond_single(): start_ast = [ parser.LabelStmt("start"), parser.AssignOp(src="inbox", dst="emp"), parser.JumpCondOp("if", "ez"), parser.OutboxOp(), parser.JumpOp("endif"), parser.LabelStmt("if"), parser.JumpOp("start"), parser.LabelStmt("endif"), parser.OutboxOp() ] expected_ast = [ parser.LabelStmt("start"), parser.AssignOp(src="inbox", dst="emp"), parser.JumpCondOp("start", "ez"), parser.OutboxOp(), parser.JumpOp("endif"), parser.LabelStmt("if"), parser.JumpOp("start"), parser.LabelStmt("endif"), parser.OutboxOp() ] ast = compress_jumps(start_ast) assert ast == expected_ast
def test_convert_iftojump_nested(): code = [ p.IfOp("ez", [ p.IfOp("ez", [p.AssignOp("inbox", "emp")], [p.OutboxOp()]), ], [ p.IfOp("neg", [p.AssignOp("inbox", "emp")], [p.OutboxOp()]) ])] expected_ast = [ p.JumpCondOp(condition="jez", label_name="_hrm_1"), p.JumpCondOp(condition="jneg", label_name="_hrm_2"), p.OutboxOp(), p.JumpOp("_hrm_endif_2"), p.LabelStmt("_hrm_2"), p.AssignOp("inbox", "emp"), p.LabelStmt("_hrm_endif_2"), p.JumpOp("_hrm_endif_1"), p.LabelStmt("_hrm_1"), p.JumpCondOp(condition="jez", label_name="_hrm_3"), p.OutboxOp(), p.JumpOp("_hrm_endif_3"), p.LabelStmt("_hrm_3"), p.AssignOp("inbox", "emp"), p.LabelStmt("_hrm_endif_3"), p.LabelStmt("_hrm_endif_1")] ast = c.convert_iftojump(code) assert ast == expected_ast
def test_unreachable_condjumps(): start_ast = [ parser.AssignOp(src="inbox", dst="emp"), parser.JumpCondOp("_hrm_1", "jez"), parser.OutboxOp(), parser.LabelStmt("_hrm_1"), parser.LabelStmt("_hrm_endif_1") ] expected_ast = [ parser.AssignOp(src="inbox", dst="emp"), parser.JumpCondOp("_hrm_unreachable", "jez"), parser.OutboxOp(), parser.LabelStmt("_hrm_unreachable") ] ast = remove_unreachable_code(start_ast) assert ast == expected_ast
def convert_if(self, ifObj): def create_adhoc_assembler(): new_assembler = Assembler() new_assembler.aliases = self.aliases new_assembler._gen_label_cnt = self._gen_label_cnt return new_assembler label_cnt = self._gen_label_cnt self._gen_label_cnt += 1 true_branch_assembler = create_adhoc_assembler() true_branch_assembler.convert(ifObj.true_branch) self._gen_label_cnt = true_branch_assembler._gen_label_cnt false_branch_assembler = create_adhoc_assembler() false_branch_assembler.convert(ifObj.false_branch) self._gen_label_cnt = false_branch_assembler._gen_label_cnt self.convert_condjump( p.JumpCondOp("_hrm_" + str(label_cnt), "j" + ifObj.condition)) for false_branch_codeline in false_branch_assembler.code: self.code.append(false_branch_codeline) self.convert_jump(p.JumpOp("_hrm_endif_" + str(label_cnt))) self.convert_label(p.LabelStmt("_hrm_" + str(label_cnt))) for true_branch_codeline in true_branch_assembler.code: self.code.append(true_branch_codeline) self.convert_label(p.LabelStmt("_hrm_endif_" + str(label_cnt)))
def test_convert_jmpthenlabel_conditionaljumps_multiple(): start_ast = [ p.JumpCondOp(condition="jneg", label_name="other"), p.JumpCondOp(condition="jez", label_name="output"), p.LabelStmt("output"), p.OutboxOp(), p.LabelStmt("other"), p.AssignOp(src="inbox", dst="emp") ] expected_ast = [ p.JumpCondOp(condition="jneg", label_name="other"), p.OutboxOp(), p.LabelStmt("other"), p.AssignOp(src="inbox", dst="emp") ] ast = conversion.fix_jmp_then_label(start_ast) assert ast == expected_ast
def test_convert_jmpthenlabel_conditionaljumps_leading_to_same_instr(): start_ast = [ p.JumpCondOp(condition="ez", label_name="output"), p.LabelStmt("output"), p.OutboxOp() ] expected_ast = [p.OutboxOp()] ast = conversion.fix_jmp_then_label(start_ast) assert ast == expected_ast
def test_dont_optimize_conditional_jumps(): """ if ez then # nothing, but the `jez` must not be removed! # if removed, the program becomes only `outbox`, # that is incorrect. else outbox endif """ start_ast = [ parser.JumpCondOp(label_name='_hrm_1', condition='jez'), parser.OutboxOp(), parser.JumpOp(label_name="_hrm_endif_1"), parser.LabelStmt(label_name="_hrm_1"), parser.LabelStmt(label_name="_hrm_endif_1") ] ast = compress_jumps(start_ast) assert parser.JumpCondOp(label_name="_hrm_1", condition="jez") in ast
def test_jcond_avoid_loop(): start_ast = [ parser.LabelStmt("test"), parser.JumpCondOp("test", "ez"), parser.OutboxOp(), parser.JumpOp("test"), ] expected_ast = start_ast ast = compress_jumps(start_ast) assert ast == expected_ast
def test_convert_iftojump_onlytruebranch_consecutive(): code = [ p.IfOp("ez", [p.OutboxOp()], []), p.IfOp("ez", [p.OutboxOp()], [])] expected_ast = [ # first IF p.JumpCondOp(condition="jez", label_name="_hrm_1"), p.JumpOp("_hrm_endif_1"), p.LabelStmt("_hrm_1"), p.OutboxOp(), p.LabelStmt("_hrm_endif_1"), # second IF p.JumpCondOp(condition="jez", label_name="_hrm_2"), p.JumpOp("_hrm_endif_2"), p.LabelStmt("_hrm_2"), p.OutboxOp(), p.LabelStmt("_hrm_endif_2")] ast = c.convert_iftojump(code) assert ast == expected_ast
def test_jcond_no_jump_in_false(): start_ast = [ parser.AssignOp("inbox", "emp"), parser.JumpCondOp(condition="ez", label_name="truebranch"), parser.OutboxOp(), parser.LabelStmt("truebranch"), parser.AddOp("tmp") ] ast = remove_unreachable_code(start_ast) assert ast == start_ast
def test_compress_jcond_no_compress(): start_ast = [ parser.JumpCondOp("label", "ez"), parser.OutboxOp(), parser.JumpOp("endif"), parser.LabelStmt("label"), parser.OutboxOp(), parser.LabelStmt("endif"), parser.OutboxOp() ] expected_ast = start_ast ast = compress_jumps(start_ast) assert ast == expected_ast
def test_no_operation_ignored_in_nonlooping_code(): start_ast = [ parser.JumpCondOp("_hrm_1", "jez"), parser.IncrOp("a_field"), parser.JumpOp("the_fence"), parser.LabelStmt("_hrm_1"), parser.IncrOp("b_field"), parser.LabelStmt("the_fence"), parser.IncrOp("c_field") ] ast = remove_unreachable_code(start_ast) assert parser.IncrOp("b_field") in start_ast assert parser.IncrOp("b_field") in ast
def test_dont_keep_only_last_label_on_instruction(): start_ast = [ parser.AssignOp(src='inbox', dst='emp'), parser.JumpCondOp(label_name='_hrm_1', condition='jez'), parser.OutboxOp(), parser.JumpOp(label_name="_hrm_endif_1"), parser.LabelStmt("_hrm_1"), parser.LabelStmt("_hrm_endif_1"), parser.AssignOp(src="inbox", dst="emp") ] ast = remove_unreachable_code(start_ast) assert parser.LabelStmt("_hrm_endif_1") in ast assert parser.LabelStmt("_hrm_1") in ast
def test_convert_iftojump_onlyfalsebranch(): code = [ p.IfOp("ez", [], [ p.OutboxOp() ])] expected_ast = [ p.JumpCondOp(condition="jez", label_name="_hrm_1"), p.OutboxOp(), p.JumpOp("_hrm_endif_1"), p.LabelStmt("_hrm_1"), p.LabelStmt("_hrm_endif_1") ] ast = c.convert_iftojump(code) assert ast == expected_ast
def test_jcond_lone_label(): start_ast = [ parser.LabelStmt("start"), parser.AssignOp(src="inbox", dst="emp"), parser.JumpCondOp("if", "ez"), parser.JumpOp("start"), parser.JumpOp("_generated_endif"), parser.LabelStmt("if"), parser.JumpOp("start"), parser.LabelStmt("_generated_endif") ] expected_ast = [ parser.LabelStmt("start"), parser.AssignOp(src="inbox", dst="emp"), parser.JumpCondOp("start", "ez"), parser.JumpOp("start"), parser.JumpOp("_generated_endif"), parser.LabelStmt("if"), parser.JumpOp("start"), parser.LabelStmt("_generated_endif") ] ast = compress_jumps(start_ast) assert ast == expected_ast
def test_unreachable_jump_to_lone_label_in_jcond_false_path(): start_ast = [ parser.LabelStmt("start"), parser.AssignOp(src="inbox", dst="emp"), parser.JumpCondOp(condition="ez", label_name="isZero"), parser.JumpOp("loneLabel"), parser.LabelStmt("isZero"), parser.OutboxOp(), parser.JumpOp("start"), parser.LabelStmt("loneLabel") ] expected_ast = [ parser.LabelStmt("start"), parser.AssignOp(src="inbox", dst="emp"), parser.JumpCondOp(condition="ez", label_name="isZero"), parser.JumpOp("_hrm_unreachable"), parser.LabelStmt("isZero"), parser.OutboxOp(), parser.JumpOp("start"), parser.LabelStmt("_hrm_unreachable") ] ast = remove_unreachable_code(start_ast) assert ast == expected_ast
def test_no_duplicated_labels_on_same_point(): start_ast = [ parser.AssignOp(src="inbox", dst="emp"), parser.JumpOp("skip"), parser.LabelStmt("comehere"), parser.OutboxOp(), parser.LabelStmt("skip"), parser.JumpOp("comehere") ] ast = remove_unreachable_code(start_ast) comehere_counter = sum(1 for ast_item in ast \ if type(ast_item) == parser.LabelStmt \ and ast_item.label_name == "comehere") assert comehere_counter == 1 start_ast = [ parser.AssignOp(src="inbox", dst="emp"), parser.JumpOp("comehere"), parser.LabelStmt("comehere"), parser.OutboxOp(), parser.LabelStmt("skip"), parser.JumpOp("comehere") ] ast = remove_unreachable_code(start_ast) comehere_counter = sum(1 for ast_item in ast \ if type(ast_item) == parser.LabelStmt \ and ast_item.label_name == "comehere") assert comehere_counter == 1 start_ast = [ parser.AssignOp(src="inbox", dst="emp"), parser.JumpCondOp("comehere", "jez"), parser.JumpOp("comehere"), parser.LabelStmt("comehere"), parser.OutboxOp(), parser.LabelStmt("skip"), parser.JumpOp("comehere") ] ast = remove_unreachable_code(start_ast) comehere_counter = sum(1 for ast_item in ast \ if type(ast_item) == parser.LabelStmt \ and ast_item.label_name == "comehere") assert comehere_counter == 1
def compress_jumps(ast): compressed_ast = [] labels_positions, label_at_pos = labels_in_ast(ast) for index, ast_item in enumerate(ast): if type(ast_item) in [p.JumpOp, p.JumpCondOp]: jump_going_nowhere = False visited = [False for i in ast] _label = ast_item.label_name try: # get the first position executable by `_label` next_pos = labels_positions[_label] except KeyError: jump_going_nowhere = True # even though a jump, either conditional or unconditional, redirects to a label # that is _not_ associated to any instruction, removing conditional # jumps alters the logic of the program compressed_ast.append(ast_item) continue while type(ast[next_pos]) == p.JumpOp and \ not visited[next_pos] and \ not jump_going_nowhere: visited[next_pos] = True _label = ast[next_pos].label_name try: next_pos = labels_positions[_label] except KeyError: jump_going_nowhere = True if type(ast_item) == p.JumpOp: compressed_ast.append(p.JumpOp(_label)) else: compressed_ast.append(p.JumpCondOp(_label, ast_item.condition)) else: compressed_ast.append(ast_item) return compressed_ast
def _convert_iftojump(ast, if_counter=0): new_ast = [] for ast_item in ast: if type(ast_item) == IfOp: if_counter = if_counter + 1 new_ast.append( p.JumpCondOp(condition="j" + ast_item.condition, label_name="_hrm_{0}".format(if_counter))) converted_false_branch, counter = _convert_iftojump( ast_item.false_branch, if_counter) for op in converted_false_branch: new_ast.append(op) new_ast.append(p.JumpOp("_hrm_endif_{0}".format(if_counter))) new_ast.append(p.LabelStmt("_hrm_{0}".format(if_counter))) converted_true_branch, counter = _convert_iftojump( ast_item.true_branch, counter) for op in converted_true_branch: new_ast.append(op) new_ast.append(p.LabelStmt("_hrm_endif_{0}".format(if_counter))) if_counter = counter else: new_ast.append(ast_item) return new_ast, if_counter
def test_jmp(): code = [parser.JumpCondOp("test", "jez")] assert get_assembly(code) == [{"operation": "jez", "operand": {"Label": "test"}}]
def remove_unreachable_code(ast): minimized_ast = [] labels_positions, label_at_pos = labels_in_ast(ast) # tree-like structure next_pointers = [None for instr in ast] visited = [False for instr in ast] assoc = [None for instr in ast] ic = 0 jcond_stack = [] last_was_jmp = False prev_ic = 0 INSTRUCTIONS_NUM = len(ast) hrm_unreachable_used = False def next_ic_in_jcond_stack(jcond_stack): while jcond_stack: jcond_ic = jcond_stack.pop() _, maybe_ic = next_pointers[jcond_ic] if maybe_ic != None: return maybe_ic return None while ic < INSTRUCTIONS_NUM or jcond_stack: # if we reached the last instruction of the program, # but we still have some unexplored `jcond`s, we must explore # these "forgotten" paths too! if not (ic < INSTRUCTIONS_NUM) and jcond_stack: ic = next_ic_in_jcond_stack(jcond_stack) or ic if ic >= INSTRUCTIONS_NUM and not jcond_stack: break # read instruction instr = ast[ic] prev_ic = ic last_was_jmp = False if not visited[ic]: # S_notvis if type(instr) == p.LabelStmt: # S_label ic += 1 else: visited[ic] = True if type(instr) == p.JumpOp: # S_jmp try: _next_pos = labels_positions[instr.label_name] if last_was_jmp: next_pointers[prev_ic] = (_next_pos, -1) else: next_pointers[ic] = (_next_pos, -1) if assoc[_next_pos] != None: assoc[_next_pos].append(instr.label_name) else: assoc[_next_pos] = [instr.label_name] ic = _next_pos last_was_jmp = True except KeyError: _next_pos = ic if last_was_jmp: next_pointers[prev_ic] = (_next_pos, -1) else: next_pointers[ic] = (_next_pos, -1) ast[ic] = p.JumpOp("_hrm_unreachable") hrm_unreachable_used = True ic = INSTRUCTIONS_NUM else: if type(instr) == p.JumpCondOp: # S_jcond jcond_stack.append(ic) try: _jcond_pos = labels_positions[instr.label_name] next_pointers[ic] = (ic + 1, _jcond_pos) ic += 1 if assoc[_jcond_pos] != None: assoc[_jcond_pos].append(instr.label_name) else: assoc[_jcond_pos] = [instr.label_name] except KeyError: _jcond_pos = None ast[ic] = p.JumpCondOp("_hrm_unreachable", instr.condition) next_pointers[ic] = (ic + 1, _jcond_pos) ic += 1 hrm_unreachable_used = True else: # S_normal next_pointers[ic] = (ic + 1, -1) ic += 1 else: if not jcond_stack: break ic = next_ic_in_jcond_stack(jcond_stack) or ic minimized_ast = [] for index, ast_item in enumerate(ast): if visited[index]: if assoc[index]: for label_name in sorted(set(assoc[index])): minimized_ast.append(p.LabelStmt(label_name)) minimized_ast.append(ast_item) if hrm_unreachable_used: minimized_ast.append(p.LabelStmt("_hrm_unreachable")) return minimized_ast