def _establish_warps(state, instructions): state.blocks[0].warpins_count = 1 enumerated_blocks = enumerate(state.blocks[:-1]) for i, block in enumerated_blocks: if state.blocks.__contains__(block) is None: continue state.block = block end_addr = block.last_address + 1 start_addr = max(block.last_address - 1, block.first_address) # Catch certain double unconditional jumps caused by logical primitives in expressions: if start_addr == (end_addr - 1) \ and end_addr + 1 < len(instructions) \ and instructions[start_addr].opcode == ins.JMP.opcode \ and instructions[end_addr].opcode == ins.JMP.opcode \ and instructions[start_addr].A == instructions[end_addr].A \ and instructions[start_addr].CD == 0: end_instruction_destination = end_addr + instructions[ end_addr].CD + 1 target_instruction_A = instructions[start_addr].A exit_instruction_found = False # When two consecutive jumps are found with the same A operand, lookahead for the end jump. following_destination = -1 for j in range(end_addr + 1, len(instructions) - 1): following_instruction = instructions[j] if following_instruction.opcode == ins.JMP.opcode: if following_instruction.A == target_instruction_A: following_destination = get_jump_destination( j, following_instruction) exit_instruction_found = True break # If we find the exit jump and we're not skipping it (if true then break else), # form the original two jumps into a fake conditional warp. if exit_instruction_found \ and end_instruction_destination <= following_destination: fixed_instruction = ins.ISF() fixed_instruction.CD = ins.SLOT_FALSE instructions[start_addr] = fixed_instruction state.blocks.pop(state.blocks.index(block) + 1) block.last_address += 1 start_addr = max(block.last_address - 1, block.first_address) end_addr = block.last_address + 1 warp = instructions[start_addr:end_addr] block.warp, shift = _build_warp(state, block.last_address, warp) setattr(block, "_last_body_addr", block.last_address - shift) setattr(block.warp, "_addr", block.last_address - shift + 1) last_block = state.blocks[-1] last_block.warp = nodes.EndWarp() setattr(last_block, "_last_body_addr", last_block.last_address) setattr(last_block.warp, "_addr", last_block.last_address)
def _fix_broken_repeat_until_loops(state, instructions): enumerated_instructions = enumerate(instructions) for i, instruction in enumerated_instructions: if instruction.opcode == ins.LOOP.opcode: # Check for the conditional jump that restarts the loop loop_exit_addr = get_jump_destination(i, instruction) loop_condition_addr = loop_exit_addr - 1 loop_condition_instruction = instructions[loop_condition_addr] if not loop_condition_instruction.opcode == ins.JMP.opcode: if get_jump_destination(loop_condition_addr, loop_condition_instruction) <= i: continue # It's not there, so this is probably a repeat-until true loop. # We need a fake conditional warp that is treated as 'true' by the writer fixed_cond_instruction = ins.ISF() fixed_cond_instruction.CD = ins.SLOT_TRUE # Resulting jump to the loop starting point fixed_jump_instruction = ins.JMP() fixed_jump_instruction.CD = i - loop_condition_addr - 1 # Add fake conditional instructions insertion_index = loop_condition_addr + 1 _insert_instruction(state, instructions, insertion_index, fixed_jump_instruction) _insert_instruction(state, instructions, insertion_index, fixed_cond_instruction) shift = 2 # Fix non-break destinations within the loop # Breaks in the empty-condition loop point towards the same exit destination # as non-breaks, so we'll have to search for a pattern of jumps. leading_jump = False start_index = i + 1 for j in range(start_index, insertion_index): checked_instruction = instructions[j] # Look for following JMP instructions if checked_instruction.opcode == ins.JMP.opcode: # Leading jump indicates this is a break? if not leading_jump: checked_instruction_destination \ = get_jump_destination(j, checked_instruction) # If the destination would've been moved if checked_instruction.CD >= shift \ and checked_instruction_destination == insertion_index + shift: # Check for an inverted jump pair next_index = j + 1 following_instruction = instructions[ next_index] if following_instruction.opcode == ins.JMP.opcode: following_destination \ = get_jump_destination(next_index, following_instruction) # e.g. goto 277 followed directly by goto 176 if following_destination < checked_instruction_destination: leading_jump = True continue # e.g. goto 277 followed directly by goto 277 elif following_destination == checked_instruction_destination: leading_jump = False continue # Check for else-break-end following this jump following_else_break_found = False prev_jump = False for k in range(next_index, insertion_index): following_instruction = instructions[k] if following_instruction.opcode == ins.JMP.opcode: if not prev_jump: prev_jump = True else: following_destination \ = get_jump_destination(k, following_instruction) # Don't adjust the checked jump, it's probably a break if following_instruction.CD >= shift \ and following_destination \ == checked_instruction_destination: following_else_break_found = True break prev_jump = False else: if prev_jump: last_destination \ = get_jump_destination(k - 1, instructions[k - 1]) # We can adjust, it's probably not a break if last_destination < checked_instruction_destination: break prev_jump = False if not following_else_break_found: checked_instruction.CD -= shift leading_jump = True else: leading_jump = False