Subrepo update (#564)

* git subrepo pull backend/asm_differ

subrepo:
  subdir:   "backend/asm_differ"
  merged:   "aad7f7d"
upstream:
  origin:   "https://github.com/simonlindholm/asm-differ"
  branch:   "main"
  commit:   "aad7f7d"
git-subrepo:
  version:  "0.4.5"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "fb52a1214"

* git subrepo pull (merge) backend/m2c

subrepo:
  subdir:   "backend/m2c"
  merged:   "9809df2"
upstream:
  origin:   "https://github.com/matt-kempster/mips_to_c"
  branch:   "master"
  commit:   "0927f17"
git-subrepo:
  version:  "0.4.5"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "fb52a1214"

* Attempt to fix download URL
This commit is contained in:
EllipticEllipsis
2022-10-18 22:52:26 +01:00
committed by GitHub
parent 09b35f5a69
commit f1fc706f14
30 changed files with 951 additions and 87 deletions

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/simonlindholm/asm-differ
branch = main
commit = 516595687dd8d9b3dd409f263269a973c38b96ea
parent = b026a2ef101e56137870e0ee91e527475f55db62
commit = aad7f7d1517517f55817fda1ce4728d8a406d099
parent = 822278a57bd2ab6ee8f8aa1f97d88aaa783e2b93
method = merge
cmdver = 0.4.3
cmdver = 0.4.5

View File

@@ -1591,6 +1591,10 @@ class AsmProcessorPPC(AsmProcessor):
class AsmProcessorARM32(AsmProcessor):
def process_reloc(self, row: str, prev: str) -> Tuple[str, Optional[str]]:
arch = self.config.arch
if "R_ARM_V4BX" in row:
# R_ARM_V4BX converts "bx <reg>" to "mov pc,<reg>" for some targets.
# Ignore for now.
return prev, None
if "R_ARM_ABS32" in row and not prev.startswith(".word"):
# Don't crash on R_ARM_ABS32 relocations incorrectly applied to code.
# (We may want to do something more fancy here that actually shows the
@@ -1739,7 +1743,6 @@ class ArchSettings:
re_reloc: Pattern[str]
branch_instructions: Set[str]
instructions_with_address_immediates: Set[str]
jump_instructions: Set[str] = field(default_factory=set)
forbidden: Set[str] = field(default_factory=lambda: set(string.ascii_letters + "_"))
arch_flags: List[str] = field(default_factory=list)
branch_likely_instructions: Set[str] = field(default_factory=set)
@@ -1776,18 +1779,6 @@ MIPS_BRANCH_INSTRUCTIONS = MIPS_BRANCH_LIKELY_INSTRUCTIONS.union(
}
)
MIPS_JUMP_ADDR_INSTRUCTIONS = {
"jal",
"j",
}
MIPS_JUMP_REG_INSTRUCTIONS = {
"jalr",
"jr",
}
MIPS_JUMP_INSTRUCTIONS = set.union(
MIPS_JUMP_ADDR_INSTRUCTIONS, MIPS_JUMP_REG_INSTRUCTIONS
)
ARM32_PREFIXES = {"b", "bl"}
ARM32_CONDS = {
"",
@@ -1946,16 +1937,15 @@ MIPS_SETTINGS = ArchSettings(
re_reg=re.compile(r"\$?\b([astv][0-9]|at|f[astv]?[0-9]+f?|kt?[01]|fp|ra|zero)\b"),
re_sprel=re.compile(r"(?<=,)([0-9]+|0x[0-9a-f]+)\(sp\)"),
re_large_imm=re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}"),
re_imm=re.compile(r"(\b|-)([0-9]+|0x[0-9a-fA-F]+)\b(?!\(sp)|%(lo|hi)\([^)]*\)"),
re_imm=re.compile(
r"(\b|-)([0-9]+|0x[0-9a-fA-F]+)\b(?!\(sp)|%(lo|hi|got|gp_rel|call16)\([^)]*\)"
),
re_reloc=re.compile(r"R_MIPS_"),
arch_flags=["-m", "mips:4300"],
branch_likely_instructions=MIPS_BRANCH_LIKELY_INSTRUCTIONS,
branch_instructions=MIPS_BRANCH_INSTRUCTIONS,
jump_instructions=MIPS_JUMP_INSTRUCTIONS,
instructions_with_address_immediates=set.union(
MIPS_BRANCH_INSTRUCTIONS, MIPS_JUMP_ADDR_INSTRUCTIONS
),
delay_slot_instructions=set.union(MIPS_BRANCH_INSTRUCTIONS, MIPS_JUMP_INSTRUCTIONS),
instructions_with_address_immediates=MIPS_BRANCH_INSTRUCTIONS.union({"j", "jal"}),
delay_slot_instructions=MIPS_BRANCH_INSTRUCTIONS.union({"j", "jal", "jr", "jalr"}),
proc=AsmProcessorMIPS,
)
@@ -2307,7 +2297,9 @@ def process(dump: str, config: Config) -> List[Line]:
row = normalize_imms(row, arch)
branch_target = None
if mnemonic in arch.branch_instructions or is_text_relative_j:
if (
mnemonic in arch.branch_instructions or is_text_relative_j
) and symbol is None:
x86_longjmp = re.search(r"\*(.*)\(", args)
if x86_longjmp:
capture = x86_longjmp.group(1)
@@ -2501,8 +2493,8 @@ def diff_sameline(
else:
# If the last field has a parenthesis suffix, e.g. "0x38(r7)"
# we split that part out to make it a separate field
# however, we don't split if it has a proceeding %hi/%lo e.g."%lo(.data)" or "%hi(.rodata + 0x10)"
re_paren = re.compile(r"(?<!%hi)(?<!%lo)\(")
# however, we don't split if it has a proceeding % macro, e.g. "%lo(.data)"
re_paren = re.compile(r"(?<!%hi)(?<!%lo)(?<!%got)(?<!%call16)(?<!%gp_rel)\(")
oldfields = oldfields[:-1] + re_paren.split(oldfields[-1])
newfields = newfields[:-1] + re_paren.split(newfields[-1])

View File

@@ -363,7 +363,7 @@ def download_n64():
print(f"ido{version} already exists, skipping")
else:
download_tar(
url=f"https://github.com/ethteck/ido-static-recomp/releases/download/master/ido-{version}-recomp-{host_os.ido_os}-latest.tar.gz",
url=f"https://github.com/ethteck/ido-static-recomp/releases/download/v0.0/ido-{version}-recomp-{host_os.ido_os}-latest.tar.gz",
dest_name=f"ido{version}",
)
# SN

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/matt-kempster/mips_to_c
branch = master
commit = a2802673e58482940990f668fdd01cab20360974
commit = 0927f17aac197848d4ebdf0c6bbad74b01f0851c
parent = 79adcdac2f60b87a955788b461d7b32819668392
method = merge
cmdver = 0.4.3
cmdver = 0.4.5

View File

@@ -36,4 +36,8 @@ typedef s64 M2C_UNK64;
/* Carry bit from partially-implemented instructions */
#define M2C_CARRY 0
/* Memcpy patterns */
#define M2C_MEMCPY_ALIGNED memcpy
#define M2C_MEMCPY_UNALIGNED memcpy
#endif

View File

@@ -12,6 +12,7 @@ from typing import (
from .error import DecompFailure
from .options import Target
from .asm_file import Label
from .asm_instruction import (
Argument,
AsmAddressMode,
@@ -32,6 +33,7 @@ from .asm_pattern import (
AsmMatch,
AsmMatcher,
AsmPattern,
Pattern,
Replacement,
SimpleAsmPattern,
make_pattern,
@@ -83,6 +85,7 @@ from .evaluate import (
handle_conditional_move,
handle_convert,
handle_la,
handle_lw,
handle_load,
handle_lwl,
handle_lwr,
@@ -469,6 +472,211 @@ class TrapuvPattern(SimpleAsmPattern):
return Replacement([m.body[2], new_instr], len(m.body))
class SetGpPattern(AsmPattern):
"""Strip PIC .cpload pattern at the top of functions."""
pattern = make_pattern(
"*",
"addiu $gp, $gp, _",
"addu $gp, $gp, $t9",
)
def match(self, matcher: AsmMatcher) -> Optional[Replacement]:
if matcher.index != 0:
return None
m = matcher.try_match(self.pattern)
if m is None:
return None
nop = AsmInstruction("nop", [])
return Replacement([nop], len(m.body))
class MemcpyPatternBase(AsmPattern):
"""IDO unrolled memcpy, used e.g. for struct copies. Slightly different patterns
are used for -mips1 vs -mips2, and for >=4 vs <4 byte alignment."""
pattern_mips1: Pattern
pattern_mips2: Pattern
epilogue1: Pattern
epilogue2: Pattern
instruction_name: str
def match(self, matcher: AsmMatcher) -> Optional[Replacement]:
for pattern in (self.pattern_mips1, self.pattern_mips2):
m = matcher.try_match(pattern)
if m:
break
else:
return None
# Find length by searching backwards for an "addiu $e, $x, ..." where
# $x = $i or there's a "move $i, $x".
length: Optional[int] = None
e_src: Optional[Instruction] = None
i_src: Optional[Instruction] = None
for i in range(matcher.index - 1, -1, -1):
item = matcher.input[i]
if isinstance(item, Label):
break
if not i_src and m.regs["i"] in item.outputs:
i_src = item
if not e_src and m.regs["e"] in item.outputs:
e_src = item
if e_src is None or e_src.mnemonic != "addiu":
return None
if e_src.args[1] != m.regs["i"] and (
i_src is None or i_src.mnemonic != "move" or i_src.args[1] != e_src.args[1]
):
return None
assert isinstance(e_src.args[2], AsmLiteral)
length = e_src.args[2].value
# Extend the pattern to include additional bytes copied when the total size
# isn't a multiple of three. (This doesn't really work well at the moment,
# see the comment in AlignedMemcpyPattern...)
added_length = length
m2 = matcher.try_match(pattern + self.epilogue1)
if m2:
m = m2
length += 4
m3 = matcher.try_match(pattern + self.epilogue1 + self.epilogue2)
if m3:
m = m3
length += 4
from_reg = Register("from")
to_reg = Register("to")
from_offset = AsmLiteral(m.literals["N"])
to_offset = AsmLiteral(m.literals["M"])
added_len_arg = AsmLiteral(added_length)
len_arg = AsmLiteral(length)
return Replacement(
[
m.body[0],
AsmInstruction("addiu", [from_reg, m.regs["i"], from_offset]),
AsmInstruction("addiu", [to_reg, m.regs["o"], to_offset]),
AsmInstruction(self.instruction_name, [to_reg, from_reg, len_arg]),
AsmInstruction("addiu", [m.regs["o"], m.regs["o"], added_len_arg]),
AsmInstruction("addiu", [m.regs["i"], m.regs["i"], added_len_arg]),
]
+ list(m.wildcard_items)
+ [AsmInstruction("nop", [])],
len(m.body),
)
class AlignedMemcpyPattern(MemcpyPatternBase):
pattern_mips1 = make_pattern(
".loop:",
"lw $a, N($i)",
"addiu $i, $i, 12",
"sw $a, M($o)",
"lw $b, (N-8)($i)",
"addiu $o, $o, 12",
"sw $b, (M-8)($o)",
"lw $c, (N-4)($i)",
"bne $i, $e, .loop",
"sw $c, (M-4)($o)",
)
pattern_mips2 = make_pattern(
".loop:",
"lw $a, N($i)",
"addiu $i, $i, 12",
"addiu $o, $o, 12",
"sw $a, MM($o)",
".eq M, MM+12",
"lw $b, (N-8)($i)",
"sw $b, (M-8)($o)",
"lw $c, (N-4)($i)",
"bne $i, $e, .loop",
"sw $c, (M-4)($o)",
)
# TODO: the * should not be there on -mips2, and the very last instruction can be
# reordered to be further down. (Luckily the effects of failing to capture this is
# just an extra word copy after the memcpy.)
epilogue1 = make_pattern(
"lw $x, N($i)",
"*",
"sw $x, M($o)",
)
epilogue2 = make_pattern(
"lw $y, (N+4)($i)",
"*",
"sw $y, (M+4)($o)",
)
instruction_name = "memcpy.aligned.fictive"
class UnalignedMemcpyPattern(MemcpyPatternBase):
pattern_mips1 = make_pattern(
".loop:",
"lwl $a, N($i)",
"lwr $a, (N+3)($i)",
"addiu $i, $i, 12",
"swl $a, M($o)",
"swr $a, (M+3)($o)",
"lwl $b, (N-8)($i)",
"lwr $b, (N-5)($i)",
"addiu $o, $o, 12",
"swl $b, (M-8)($o)",
"swr $b, (M-5)($o)",
"lwl $c, (N-4)($i)",
"lwr $c, (N-1)($i)",
"nop",
"swl $c, (M-4)($o)",
"bne $i, $e, .loop",
"swr $c, (M-1)($o)",
)
pattern_mips2 = make_pattern(
".loop:",
"lwl $a, N($i)",
"lwr $a, (N+3)($i)",
"addiu $i, $i, 12",
"addiu $o, $o, 12",
"swl $a, MM($o)",
".eq M, MM+12",
"swr $a, (M-9)($o)",
"lwl $b, (N-8)($i)",
"lwr $b, (N-5)($i)",
"swl $b, (M-8)($o)",
"swr $b, (M-5)($o)",
"lwl $c, (N-4)($i)",
"lwr $c, (N-1)($i)",
"swl $c, (M-4)($o)",
"bne $i, $e, .loop",
"swr $c, (M-1)($o)",
)
epilogue1 = make_pattern(
"lwl $x, N($i)",
"lwr $x, (N+3)($i)",
"*",
"swl $x, M($o)",
"swr $x, (M+3)($o)",
)
epilogue2 = make_pattern(
"lwl $y, (N+4)($i)",
"lwr $y, (N+7)($i)",
"*",
"swl $y, (M+4)($o)",
"swr $y, (M+7)($o)",
)
instruction_name = "memcpy.unaligned.fictive"
class GpJumpPattern(SimpleAsmPattern):
"""Remove additions of $gp used for position-independent jump tables. It's
possible that this could be done for all additions of $gp, not just jumps,
but we are conservative for now."""
pattern = make_pattern(
"addu $x, $x, $gp",
"jr $x",
)
def replace(self, m: AsmMatch) -> Replacement:
return Replacement([m.body[1]], len(m.body))
class MipsArch(Arch):
arch = Target.ArchEnum.MIPS
@@ -680,7 +888,7 @@ class MipsArch(Arch):
clobbers: List[Location] = []
outputs: List[Location] = []
jump_target: Optional[Union[JumpTarget, Register]] = None
function_target: Optional[Union[AsmGlobalSymbol, Register]] = None
function_target: Optional[Argument] = None
has_delay_slot = False
is_branch_likely = False
is_conditional = False
@@ -732,7 +940,7 @@ class MipsArch(Arch):
eval_fn = lambda s, a: s.set_switch_expr(a.reg(0))
elif mnemonic == "jal" or mnemonic == "bal":
# Function call to label
assert len(args) == 1 and isinstance(args[0], AsmGlobalSymbol)
assert len(args) == 1
inputs = list(cls.argument_regs)
outputs = list(cls.all_return_regs)
clobbers = list(cls.temp_regs)
@@ -1021,6 +1229,10 @@ class MipsArch(Arch):
Mips1DoubleLoadStorePattern(),
GccSqrtPattern(),
TrapuvPattern(),
SetGpPattern(),
AlignedMemcpyPattern(),
UnalignedMemcpyPattern(),
GpJumpPattern(),
]
instrs_ignore: Set[str] = {
@@ -1099,6 +1311,12 @@ class MipsArch(Arch):
),
"sync": lambda a: void_fn_op("M2C_SYNC", []),
"trapuv.fictive": lambda a: CommentStmt("code compiled with -trapuv"),
"memcpy.aligned.fictive": lambda a: void_fn_op(
"M2C_MEMCPY_ALIGNED", [a.reg(0), a.reg(1), a.imm(2)]
),
"memcpy.unaligned.fictive": lambda a: void_fn_op(
"M2C_MEMCPY_UNALIGNED", [a.reg(0), a.reg(1), a.imm(2)]
),
}
instrs_float_comp: InstrMap = {
# Float comparisons that don't raise exception on nan
@@ -1340,7 +1558,7 @@ class MipsArch(Arch):
"lbu": lambda a: handle_load(a, type=Type.u8()),
"lh": lambda a: handle_load(a, type=Type.s16()),
"lhu": lambda a: handle_load(a, type=Type.u16()),
"lw": lambda a: handle_load(a, type=Type.reg32(likely_float=False)),
"lw": lambda a: handle_lw(a),
"ld": lambda a: handle_load(a, type=Type.reg64(likely_float=False)),
"lwu": lambda a: handle_load(a, type=Type.u32()),
"lwc1": lambda a: handle_load(a, type=Type.reg32(likely_float=True)),

View File

@@ -631,7 +631,7 @@ class PpcArch(Arch):
clobbers: List[Location] = []
outputs: List[Location] = []
jump_target: Optional[Union[JumpTarget, Register]] = None
function_target: Optional[Union[AsmGlobalSymbol, Register]] = None
function_target: Optional[Argument] = None
is_conditional = False
is_return = False
is_store = False
@@ -711,7 +711,7 @@ class PpcArch(Arch):
eval_fn = lambda s, a: s.set_switch_expr(a.regs[Register("ctr")])
elif mnemonic == "bl":
# Function call to label
assert len(args) == 1 and isinstance(args[0], AsmGlobalSymbol)
assert len(args) == 1
inputs = list(cls.argument_regs)
outputs = list(cls.all_return_regs)
clobbers = list(cls.temp_regs)

View File

@@ -281,7 +281,7 @@ def parse_arg_elems(
assert value is None
arg_elems.pop(0)
word = parse_word(arg_elems)
if word in ["data", "rodata", "bss", "text"]:
if word in ["data", "rodata", "rdata", "bss", "sbss", "text"]:
value = asm_section_global_symbol(word, 0)
else:
value = JumpTarget(word)
@@ -290,7 +290,7 @@ def parse_arg_elems(
assert value is None
arg_elems.pop(0)
macro_name = parse_word(arg_elems)
assert macro_name in ("hi", "lo")
assert macro_name in ("hi", "lo", "got", "gp_rel", "call16")
expect("(")
# Get the argument of the macro (which must exist).
m = parse_arg_elems(

View File

@@ -53,6 +53,7 @@ class Replacement:
@dataclass
class AsmMatch:
body: List[BodyPart]
wildcard_items: List[BodyPart]
regs: Dict[str, Register]
literals: Dict[str, int]
@@ -85,6 +86,7 @@ class TryMatchState:
symbolic_registers: Dict[str, Register] = field(default_factory=dict)
symbolic_labels: Dict[str, str] = field(default_factory=dict)
symbolic_literals: Dict[str, int] = field(default_factory=dict)
wildcard_items: List[BodyPart] = field(default_factory=list)
T = TypeVar("T")
@@ -131,12 +133,14 @@ class TryMatchState:
return isinstance(a, AsmLiteral) and self.match_var(
self.symbolic_literals, e.symbol_name, a.value
)
elif e.symbol_name == "_":
return True
else:
return isinstance(a, AsmGlobalSymbol) and a.symbol_name == e.symbol_name
if isinstance(e, AsmAddressMode):
return (
isinstance(a, AsmAddressMode)
and a.lhs == e.lhs
and self.match_arg(a.lhs, e.lhs)
and self.match_reg(a.rhs, e.rhs)
)
if isinstance(e, JumpTarget):
@@ -149,6 +153,7 @@ class TryMatchState:
def match_one(self, actual: BodyPart, exp: PatternPart) -> bool:
if exp is None:
self.wildcard_items.append(actual)
return True
if isinstance(exp, Label):
return isinstance(actual, Label) and self.match_var(
@@ -167,6 +172,11 @@ class TryMatchState:
return False
return True
def match_meta(self, ins: AsmInstruction) -> bool:
assert ins.mnemonic == ".eq"
res = self.eval_math(ins.args[1])
return self.match_arg(AsmLiteral(res), ins.args[0])
@dataclass
class AsmMatcher:
@@ -179,12 +189,16 @@ class AsmMatcher:
start_index = index = self.index
for (pat, optional) in pattern:
if index < len(self.input) and state.match_one(self.input[index], pat):
if isinstance(pat, AsmInstruction) and pat.mnemonic[0] == ".":
if not state.match_meta(pat) and not optional:
return None
elif index < len(self.input) and state.match_one(self.input[index], pat):
index += 1
elif not optional:
return None
return AsmMatch(
self.input[start_index:index],
state.wildcard_items,
state.symbolic_registers,
state.symbolic_literals,
)

View File

@@ -194,8 +194,17 @@ def handle_la(args: InstrArgs) -> Expression:
)
)
var = stack_info.global_info.address_of_gsym(target.sym.symbol_name)
return add_imm(output_reg, var, Literal(target.offset), args)
sym = stack_info.global_info.address_of_gsym(target.sym.symbol_name)
return add_imm(output_reg, sym, Literal(target.offset), args)
def handle_lw(args: InstrArgs) -> Expression:
ref = args.maybe_got_imm(1)
if ref is not None:
# Handle `lw $a, %got(x + offset)($gp)` as an address load rather than a load.
sym = args.stack_info.global_info.address_of_gsym(ref.sym.symbol_name)
return add_imm(args.reg_ref(0), sym, Literal(ref.offset), args)
return handle_load(args, type=Type.reg32(likely_float=False))
def handle_or(left: Expression, right: Expression) -> Expression:
@@ -418,17 +427,22 @@ def handle_lwl(args: InstrArgs) -> Expression:
def handle_lwr(args: InstrArgs) -> Expression:
# Unaligned load for the right part of a register. This lwr may merge with an
# existing lwl, if it loads from the same target but with an offset that's +3.
# existing lwl, if it loads from the same target but with an offset that's ±3.
uw_old_value = early_unwrap(args.reg(0))
ref = args.memory_ref(1)
lwl_key: Tuple[int, object]
delta = -3 if args.stack_info.global_info.target.is_big_endian() else 3
if isinstance(ref, AddressMode):
lwl_key = (ref.offset - 3, args.regs[ref.rhs])
lwl_key = (ref.offset + delta, args.regs[ref.rhs])
else:
lwl_key = (ref.offset - 3, ref.sym)
lwl_key = (ref.offset + delta, ref.sym)
if isinstance(uw_old_value, Lwl) and uw_old_value.key[0] == lwl_key[0]:
return UnalignedLoad(uw_old_value.load_expr)
if ref.offset % 4 == 2:
# IDO may copy 3 bytes between 4-byte-aligned addresses using lwr+swr, e.g. for
# the purpose of array initializers. Little endian can use lwl+swl instead,
# but other compilers don't seem to emit this pattern so we don't handle that
# at the moment.
if ref.offset % 4 == 2 and args.stack_info.global_info.target.is_big_endian():
left_mem_ref = replace(ref, offset=ref.offset - 2)
load_expr = deref_unaligned(left_mem_ref, args.regs, args.stack_info)
return Load3Bytes(load_expr)

View File

@@ -298,6 +298,8 @@ def normalize_likely_branches(function: Function, arch: ArchFlowGraph) -> Functi
and before_target is next_item
and item.mnemonic != "b"
):
# Handle the GCC pattern where the branch likely crosses just a single
# instruction.
mn_inverted = invert_branch_mnemonic(item.mnemonic[:-1])
item = arch.parse(mn_inverted, item.args, item.meta.derived())
new_nop = arch.parse("nop", [], item.meta.derived())
@@ -311,8 +313,9 @@ def normalize_likely_branches(function: Function, arch: ArchFlowGraph) -> Functi
and str(before_target) == str(next_item)
and (item.mnemonic != "b" or next_item.mnemonic != "nop")
):
# Handle the IDO pattern.
if id(before_target) not in label_before_instr:
new_label = old_label + "_before"
new_label = f"_m2c_{old_label}_before"
label_before_instr[id(before_target)] = new_label
insert_label_before[id(before_target)] = new_label
new_target = JumpTarget(label_before_instr[id(before_target)])
@@ -324,6 +327,7 @@ def normalize_likely_branches(function: Function, arch: ArchFlowGraph) -> Functi
new_body.append((orig_item, item))
new_body.append((orig_next_item, next_item))
else:
# Fall back to not transforming the branch likely at all.
new_body.append((item, item))
new_body.append((next_item, next_item))
else:
@@ -405,13 +409,9 @@ def build_blocks(
assert isinstance(next_item, Instruction), "Cannot have two labels in a row"
# Best-effort check for whether the instruction can be executed twice in a row.
# TODO: This may be able to be improved to use the other fields in Instruction?
r = next_item.args[0] if next_item.args else None
if all(a != r for a in next_item.args[1:]):
process_after.append(label)
process_after.append(next_item.clone())
else:
if item.is_branch_likely or item.function_target is not None:
# (we could handle these cases too with some care, they're just very
# rare so we don't bother)
msg = [
f"Label {label.name} refers to a delay slot; this is currently not supported.",
"Please modify the assembly to work around it (e.g. copy the instruction",
@@ -426,6 +426,40 @@ def build_blocks(
"execute its delay slot unconditionally.",
]
raise DecompFailure("\n".join(msg))
elif (
all(x not in next_item.inputs for x in next_item.outputs)
and not next_item.is_store
):
# It should be okay to execute this instruction twice. This check isn't
# technically required, but it avoids creating new blocks which could
# throw if_statements off guard.
process_after.append(label)
process_after.append(next_item.clone())
else:
target = item.jump_target
assert isinstance(target, JumpTarget), "has delay slot and isn't a call"
temp_label = JumpTarget(f"_m2c_{label.name}_skip")
meta = item.meta.derived()
nop = arch.parse("nop", [], meta)
block_builder.add_instruction(
arch.parse(item.mnemonic, item.args[:-1] + [temp_label], item.meta)
)
block_builder.add_instruction(nop)
block_builder.new_block()
block_builder.add_instruction(
arch.parse("b", [JumpTarget(label.name)], item.meta.derived())
)
block_builder.add_instruction(nop.clone())
block_builder.new_block()
block_builder.set_label(Label(temp_label.target))
block_builder.add_instruction(
arch.parse("b", [target], item.meta.derived())
)
block_builder.add_instruction(next_item.clone())
block_builder.new_block()
block_builder.set_label(label)
block_builder.add_instruction(next_item)
return
if next_item.has_delay_slot:
raise DecompFailure(
@@ -438,7 +472,7 @@ def build_blocks(
branch_likely_counts[target.target] += 1
index = branch_likely_counts[target.target]
mn_inverted = invert_branch_mnemonic(item.mnemonic[:-1])
temp_label = JumpTarget(f"{target.target}_branchlikelyskip_{index}")
temp_label = JumpTarget(f"_m2c_{target.target}_branchlikelyskip_{index}")
branch_not = arch.parse(
mn_inverted, item.args[:-1] + [temp_label], item.meta.derived()
)
@@ -454,11 +488,10 @@ def build_blocks(
block_builder.new_block()
block_builder.set_label(Label(temp_label.target))
block_builder.add_instruction(nop.clone())
elif item.function_target is not None:
# Move the delay slot instruction to before the call so it
# passes correct arguments.
if next_item.args and next_item.args[0] == item.args[0]:
if len(item.args) >= 2 and item.args[1] in next_item.outputs:
raise DecompFailure(
f"Instruction after {item.mnemonic} clobbers its source\n"
"register, which is currently not supported.\n\n"
@@ -488,7 +521,7 @@ def build_blocks(
if item.is_conditional and item.is_return:
if cond_return_target is None:
cond_return_target = JumpTarget(f"_conditionalreturn_")
cond_return_target = JumpTarget(f"_m2c_conditionalreturn_")
# Strip the "lr" off of the instruction
assert item.mnemonic[-2:] == "lr"
branch_instr = arch.parse(

View File

@@ -123,7 +123,7 @@ class Instruction:
eval_fn: Optional[Callable[..., object]]
jump_target: Optional[Union[JumpTarget, Register]] = None
function_target: Optional[Union[AsmGlobalSymbol, Register]] = None
function_target: Optional[Argument] = None
is_conditional: bool = False
is_return: bool = False
is_store: bool = False

View File

@@ -1,4 +1,5 @@
import argparse
import gc
import re
import sys
import traceback
@@ -453,7 +454,7 @@ def parse_flags(flags: List[str]) -> Options:
type=Target.parse,
default="mips-ido-c",
help="Target architecture, compiler, and language triple. "
"Supported triples: mips-ido-c, mips-gcc-c, ppc-mwcc-c++, ppc-mwcc-c. "
"Supported triples: mips-ido-c, mips-gcc-c, mipsel-gcc-c, ppc-mwcc-c++, ppc-mwcc-c. "
"Default is mips-ido-c, `ppc` is an alias for ppc-mwcc-c++. ",
)
group.add_argument(
@@ -514,6 +515,12 @@ def parse_flags(flags: List[str]) -> Options:
"See the README for more information on unknown inference."
),
)
group.add_argument(
"--heuristic-strings",
dest="heuristic_strings",
action="store_true",
help="Heuristically detect strings in rodata even when not defined using .asci/.asciz.",
)
group.add_argument(
"--reg-vars",
metavar="REGISTERS",
@@ -537,6 +544,13 @@ def parse_flags(flags: List[str]) -> Options:
action="store_true",
help=argparse.SUPPRESS,
)
group.add_argument(
"--disable-gc",
dest="disable_gc",
action="store_true",
help="Disable Python garbage collection. Can improve performance at "
"the risk of running out of memory.",
)
args = parser.parse_args(flags)
reg_vars = args.reg_vars.split(",") if args.reg_vars else []
@@ -578,6 +592,7 @@ def parse_flags(flags: List[str]) -> Options:
andor_detection=args.andor_detection,
skip_casts=args.skip_casts,
zfill_constants=args.zfill_constants,
heuristic_strings=args.heuristic_strings,
reg_vars=reg_vars,
goto_patterns=args.goto_patterns,
stop_on_error=args.stop_on_error,
@@ -598,6 +613,7 @@ def parse_flags(flags: List[str]) -> Options:
passes=args.passes,
incbin_dirs=args.incbin_dirs,
deterministic_vars=args.deterministic_vars,
disable_gc=args.disable_gc,
)
@@ -606,6 +622,9 @@ def main() -> None:
# CPython default. Cap to INT_MAX to avoid an OverflowError, though.
sys.setrecursionlimit(min(2**31 - 1, 10 * sys.getrecursionlimit()))
options = parse_flags(sys.argv[1:])
if options.disable_gc:
gc.disable()
gc.set_threshold(0)
sys.exit(run(options))

View File

@@ -36,6 +36,10 @@ class Target:
MIPS = "mips"
PPC = "ppc"
class EndianEnum(ChoicesEnum):
LITTLE = "little"
BIG = "big"
class CompilerEnum(ChoicesEnum):
IDO = "ido"
GCC = "gcc"
@@ -46,9 +50,13 @@ class Target:
CXX = "c++"
arch: ArchEnum
endian: EndianEnum
compiler: CompilerEnum
language: LanguageEnum
def is_big_endian(self) -> bool:
return self.endian == Target.EndianEnum.BIG
@staticmethod
def parse(name: str) -> "Target":
"""
@@ -57,9 +65,14 @@ class Target:
If `-compiler` is missing, use the default for the arch.
(This makes `mips` an alias for `mips-ido-c`, etc.)
"""
endian = Target.EndianEnum.BIG
terms = name.split("-")
try:
arch = Target.ArchEnum(terms[0])
arch_name = terms[0]
if arch_name.endswith("el"):
arch_name = arch_name[:-2]
endian = Target.EndianEnum.LITTLE
arch = Target.ArchEnum(arch_name)
if len(terms) >= 2:
compiler = Target.CompilerEnum(terms[1])
elif arch == Target.ArchEnum.PPC:
@@ -78,6 +91,7 @@ class Target:
return Target(
arch=arch,
endian=endian,
compiler=compiler,
language=language,
)
@@ -103,6 +117,7 @@ class Options:
andor_detection: bool
skip_casts: bool
zfill_constants: bool
heuristic_strings: bool
reg_vars: List[str]
goto_patterns: List[str]
stop_on_error: bool
@@ -123,12 +138,14 @@ class Options:
passes: int
incbin_dirs: List[Path]
deterministic_vars: bool
disable_gc: bool
def formatter(self) -> "Formatter":
return Formatter(
self.coding_style,
skip_casts=self.skip_casts,
zfill_constants=self.zfill_constants,
heuristic_strings=self.heuristic_strings,
valid_syntax=self.valid_syntax,
)
@@ -156,6 +173,7 @@ class Formatter:
valid_syntax: bool = False
line_length: int = 80
zfill_constants: bool = False
heuristic_strings: bool = False
def indent(self, line: str, indent: int = 0) -> str:
return self.indent_step * max(indent + self.extra_indent, 0) + line

View File

@@ -1467,6 +1467,9 @@ class StructAccess(Expression):
if (
isinstance(var, AddressOf)
and not var.expr.type.is_array()
and not (
isinstance(var.expr, GlobalSymbol) and var.expr.is_string_constant(fmt)
)
and field_name.startswith("->")
):
var = var.expr
@@ -1509,14 +1512,27 @@ class GlobalSymbol(Expression):
def dependencies(self) -> List[Expression]:
return []
def is_string_constant(self) -> bool:
def is_likely_char(self, c: int) -> bool:
return 0x20 <= c < 0x7F or c in (0, 7, 8, 9, 10, 13, 27)
def is_string_constant(self, fmt: Formatter) -> bool:
ent = self.asm_data_entry
if not ent or not ent.is_string:
if not ent or len(ent.data) != 1 or not isinstance(ent.data[0], bytes):
return False
return len(ent.data) == 1 and isinstance(ent.data[0], bytes)
if ent.is_string:
return True
if (
fmt.heuristic_strings
and ent.is_readonly
and len(ent.data[0]) > 1
and ent.data[0][0] != 0
and all(self.is_likely_char(x) for x in ent.data[0])
):
return True
return False
def format_string_constant(self, fmt: Formatter) -> str:
assert self.is_string_constant(), "checked by caller"
assert self.is_string_constant(fmt), "checked by caller"
assert self.asm_data_entry and isinstance(self.asm_data_entry.data[0], bytes)
has_trailing_null = False
@@ -1633,7 +1649,7 @@ class AddressOf(Expression):
def format(self, fmt: Formatter) -> str:
if isinstance(self.expr, GlobalSymbol):
if self.expr.is_string_constant():
if self.expr.is_string_constant(fmt):
return self.expr.format_string_constant(fmt)
if self.expr.type.is_array():
return f"{self.expr.format(fmt)}"
@@ -2366,16 +2382,35 @@ class InstrArgs:
raise DecompFailure(f"Invalid macro argument {arg.argument}")
return ref
def maybe_got_imm(self, index: int) -> Optional[RawSymbolRef]:
arg = self.raw_arg(index)
if not isinstance(arg, AsmAddressMode) or arg.rhs != Register("gp"):
return None
val = arg.lhs
if not isinstance(val, Macro) or val.macro_name not in (
"got",
"gp_rel",
"call16",
):
return None
ref = parse_symbol_ref(val.argument)
if ref is None:
raise DecompFailure(f"Invalid macro argument {val.argument}")
return ref
def shifted_imm(self, index: int) -> Expression:
# TODO: Should this be part of hi_imm? Do we need to handle @ha?
raw_imm = self.unsigned_imm(index)
assert isinstance(raw_imm, Literal)
return Literal(raw_imm.value << 16)
def sym_imm(self, index: int) -> AddressOf:
def sym_imm(self, index: int) -> Expression:
arg = self.raw_arg(index)
assert isinstance(arg, AsmGlobalSymbol)
return self.stack_info.global_info.address_of_gsym(arg.symbol_name)
if isinstance(arg, AsmGlobalSymbol):
return self.stack_info.global_info.address_of_gsym(arg.symbol_name)
if isinstance(arg, AsmLiteral):
return self.full_imm(index)
raise DecompFailure(f"Bad function call operand {arg}")
def memory_ref(self, index: int) -> Union[AddressMode, RawSymbolRef]:
ret = strip_macros(self.raw_arg(index))
@@ -4091,7 +4126,7 @@ class GlobalInfo:
comments.append("const")
# Float & string constants are almost always inlined and can be omitted
if sym.is_string_constant():
if sym.is_string_constant(fmt):
continue
if array_dim is None and sym.type.is_likely_float():
continue

View File

@@ -26,6 +26,7 @@ class PathsToBinaries:
IDO_CC: Optional[Path]
SM64_TOOLS: Optional[Path]
MWCC_CC: Optional[Path]
WINE: Optional[Path]
def get_environment_variables() -> PathsToBinaries:
@@ -54,7 +55,11 @@ def get_environment_variables() -> PathsToBinaries:
MWCC_CC = load(
"MWCC_CC", "env variable MWCC_CC should point to a PPC cc binary (mwcceppc.exe)"
)
return PathsToBinaries(IDO_CC=IDO_CC, SM64_TOOLS=SM64_TOOLS, MWCC_CC=MWCC_CC)
if MWCC_CC and sys.platform.startswith("linux"):
WINE = load("WINE", "env variable WINE should point to wine or wibo binary")
return PathsToBinaries(
IDO_CC=IDO_CC, SM64_TOOLS=SM64_TOOLS, MWCC_CC=MWCC_CC, WINE=WINE
)
@dataclass
@@ -66,8 +71,7 @@ class Compiler:
return replace(self, cc_command=self.cc_command + flags)
def get_compilers(paths: PathsToBinaries) -> List[Tuple[str, Compiler]]:
compilers: List[Tuple[str, Compiler]] = []
def get_ido_compilers(paths: PathsToBinaries) -> List[Tuple[str, Compiler]]:
if paths.IDO_CC is not None and paths.SM64_TOOLS is not None:
ido = Compiler(
name="ido",
@@ -86,13 +90,17 @@ def get_compilers(paths: PathsToBinaries) -> List[Tuple[str, Compiler]]:
"-signed",
],
)
compilers.append(("irix-g", ido.with_cc_flags(["-g", "-mips2"])))
compilers.append(("irix-o2", ido.with_cc_flags(["-O2", "-mips2"])))
# compilers.append(("irix-g-mips1", ido.with_cc_flags(["-O2", "-mips1"])))
# compilers.append(("irix-o2-mips1", ido.with_cc_flags(["-O2", "-mips1"])))
else:
logger.warning("IDO tools not found; skipping MIPS compilers")
return [
("irix-g", ido.with_cc_flags(["-g", "-mips2"])),
("irix-o2", ido.with_cc_flags(["-O2", "-mips2"])),
# ("irix-g-mips1", ido.with_cc_flags(["-O2", "-mips1"]))
# ("irix-o2-mips1", ido.with_cc_flags(["-O2", "-mips1"]))
]
logger.warning("IDO tools not found; skipping MIPS compilers")
return []
def get_mwcc_compilers(paths: PathsToBinaries) -> List[Tuple[str, Compiler]]:
if paths.MWCC_CC is not None:
cc_command = [
str(paths.MWCC_CC),
@@ -107,17 +115,29 @@ def get_compilers(paths: PathsToBinaries) -> List[Tuple[str, Compiler]]:
"int",
"-nodefaults",
]
ok = True
if paths.MWCC_CC.suffix == ".exe" and sys.platform.startswith("linux"):
cc_command.insert(0, "/usr/bin/wine")
mwcc = Compiler(
name="mwcc",
cc_command=cc_command,
)
compilers.append(("mwcc-o4p", mwcc.with_cc_flags(["-O4,p"])))
# compilers.append(("mwcc-o4p-s0", mwcc.with_cc_flags(["-O4,p", "-sdata", "0", "-sdata2", "0"])))
else:
logger.warning("MWCC tools not found; skipping PPC compilers")
if paths.WINE:
cc_command.insert(0, str(paths.WINE))
else:
ok = False
if ok:
mwcc = Compiler(
name="mwcc",
cc_command=cc_command,
)
return [
("mwcc-o4p", mwcc.with_cc_flags(["-O4,p"])),
# ("mwcc-o4p-s0", mwcc.with_cc_flags(["-O4,p", "-sdata", "0", "-sdata2", "0"]))
]
logger.warning("MWCC tools not found; skipping PPC compilers")
return []
def get_compilers(paths: PathsToBinaries) -> List[Tuple[str, Compiler]]:
compilers: List[Tuple[str, Compiler]] = []
compilers.extend(get_ido_compilers(paths))
compilers.extend(get_mwcc_compilers(paths))
return compilers

View File

@@ -0,0 +1,13 @@
s32 test(s32 arg0, s32 arg1) {
s32 var_a0;
var_a0 = arg0;
if (arg1 != 0) {
do {
var_a0 -= 1;
} while (var_a0 != 0);
} else {
var_a0 -= 1;
}
return var_a0;
}

View File

@@ -0,0 +1,9 @@
glabel test
beqz $a1, .skip
.loop:
addiu $a0, $a0, -1
bnez $a0, .loop
nop
.skip:
jr $ra
move $v0, $a0

View File

@@ -0,0 +1,20 @@
? func(); /* extern */
extern ? global_sym;
extern ? local_sym;
void test(u32 arg0) {
s32 var_t2;
var_t2 = 0;
switch (arg0) {
case 0:
var_t2 = 1;
break;
case 1:
var_t2 = 2;
break;
}
local_sym.unk8 = var_t2;
global_sym.unk8 = 0;
func();
}

View File

@@ -0,0 +1,41 @@
glabel test
li $t2, 0
sltiu $at, $a0, 2
beqz $at, .skip
lw $at, %got(jtbl_1)($gp)
sll $a0, $a0, 2
addu $at, $at, $a0
lw $a0, %lo(jtbl_1)($at)
addu $a0, $a0, $gp
jr $a0
nop
.case0:
b .skip
li $t2, 1
.case1:
b .skip
li $t2, 2
.skip:
lw $t1, %got(local_sym)($gp)
addiu $t1, $t1, %lo(local_sym)
sw $t2, 8($t1)
lw $t1, %got(global_sym)($gp)
sw $zero, 8($t1)
lw $t9, %got(func)($gp)
jalr $t9
nop
jr $ra
nop
.section .rodata
jtbl_1:
.word .case0
.word .case1

View File

@@ -17,9 +17,9 @@ void test(void) {
// The last 3 bytes are loaded using lwr, then stored using swr
char str[7] = "abcdef";
foo(str);
// 4 bytes being loaded using lwl+lwr, then stored using swl/swr
// 4 bytes being loaded using lwl+lwr, then stored using swl+swr
a1.data = a2.data;
// 5 bytes being loaded using lwl+lwr+lbu, then stored using sw+sb
// 5 bytes being loaded using lw+lbu, then stored using swl+swr+sb
a3[0] = a1;
// 4 bytes being loaded using lwl+lwr, then stored using sw
strcpy(buf, "ghi");

View File

@@ -0,0 +1,9 @@
extern ? D_410160;
extern ? D_4102F0;
void test(s32 arg0, s32 arg1) {
M2C_MEMCPY_ALIGNED(&D_410160, &D_4102F0, 0x18C);
*(&D_410160 + 0x18C) = *(&D_4102F0 + 0x18C);
M2C_MEMCPY_UNALIGNED(arg0, arg1, 0x60);
*(arg0 + 0x60) = (unaligned s32) *(arg1 + 0x60);
}

View File

@@ -0,0 +1,52 @@
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
glabel test
/* 0000B0 004000B0 3C0F0041 */ lui $t7, %hi(D_4102F0)
/* 0000B4 004000B4 25EF02F0 */ addiu $t7, $t7, %lo(D_4102F0)
/* 0000B8 004000B8 3C0E0041 */ lui $t6, %hi(D_410160)
/* 0000BC 004000BC 25CE0160 */ addiu $t6, $t6, %lo(D_410160)
/* 0000C0 004000C0 25E8018C */ addiu $t0, $t7, 0x18c
.L004000C4:
/* 0000C4 004000C4 8DE10000 */ lw $at, ($t7)
/* 0000C8 004000C8 25EF000C */ addiu $t7, $t7, 0xc
/* 0000CC 004000CC ADC10000 */ sw $at, ($t6)
/* 0000D0 004000D0 8DE1FFF8 */ lw $at, -8($t7)
/* 0000D4 004000D4 25CE000C */ addiu $t6, $t6, 0xc
/* 0000D8 004000D8 ADC1FFF8 */ sw $at, -8($t6)
/* 0000DC 004000DC 8DE1FFFC */ lw $at, -4($t7)
/* 0000E0 004000E0 15E8FFF8 */ bne $t7, $t0, .L004000C4
/* 0000E4 004000E4 ADC1FFFC */ sw $at, -4($t6)
/* 0000E8 004000E8 8DE10000 */ lw $at, ($t7)
/* 0000EC 004000EC 00A06025 */ move $t4, $a1
/* 0000F0 004000F0 00806825 */ move $t5, $a0
/* 0000F4 004000F4 24AB0060 */ addiu $t3, $a1, 0x60
/* 0000F8 004000F8 ADC10000 */ sw $at, ($t6)
.L004000FC:
/* 0000FC 004000FC 89810000 */ lwl $at, ($t4)
/* 000100 00400100 99810003 */ lwr $at, 3($t4)
/* 000104 00400104 258C000C */ addiu $t4, $t4, 0xc
/* 000108 00400108 A9A10000 */ swl $at, ($t5)
/* 00010C 0040010C B9A10003 */ swr $at, 3($t5)
/* 000110 00400110 8981FFF8 */ lwl $at, -8($t4)
/* 000114 00400114 9981FFFB */ lwr $at, -5($t4)
/* 000118 00400118 25AD000C */ addiu $t5, $t5, 0xc
/* 00011C 0040011C A9A1FFF8 */ swl $at, -8($t5)
/* 000120 00400120 B9A1FFFB */ swr $at, -5($t5)
/* 000124 00400124 8981FFFC */ lwl $at, -4($t4)
/* 000128 00400128 9981FFFF */ lwr $at, -1($t4)
/* 00012C 0040012C 00000000 */ nop
/* 000130 00400130 A9A1FFFC */ swl $at, -4($t5)
/* 000134 00400134 158BFFF1 */ bne $t4, $t3, .L004000FC
/* 000138 00400138 B9A1FFFF */ swr $at, -1($t5)
/* 00013C 0040013C 89810000 */ lwl $at, ($t4)
/* 000140 00400140 99810003 */ lwr $at, 3($t4)
/* 000144 00400144 00000000 */ nop
/* 000148 00400148 A9A10000 */ swl $at, ($t5)
/* 00014C 0040014C 03E00008 */ jr $ra
/* 000150 00400150 B9A10003 */ swr $at, 3($t5)
/* 000154 00400154 00000000 */ nop
/* 000158 00400158 00000000 */ nop
/* 00015C 0040015C 00000000 */ nop

View File

@@ -0,0 +1,9 @@
extern ? D_410150;
extern ? D_4102E0;
void test(s32 arg0, s32 arg1) {
M2C_MEMCPY_ALIGNED(&D_410150, &D_4102E0, 0x18C);
*(&D_410150 + 0x18C) = *(&D_4102E0 + 0x18C);
M2C_MEMCPY_UNALIGNED(arg0, arg1, 0x60);
*(arg0 + 0x60) = (unaligned s32) *(arg1 + 0x60);
}

View File

@@ -0,0 +1,48 @@
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
glabel test
/* 0000B0 004000B0 3C0F0041 */ lui $t7, %hi(D_4102E0)
/* 0000B4 004000B4 25EF02E0 */ addiu $t7, $t7, %lo(D_4102E0)
/* 0000B8 004000B8 3C0E0041 */ lui $t6, %hi(D_410150)
/* 0000BC 004000BC 25CE0150 */ addiu $t6, $t6, %lo(D_410150)
/* 0000C0 004000C0 25E8018C */ addiu $t0, $t7, 0x18c
.L004000C4:
/* 0000C4 004000C4 8DE10000 */ lw $at, ($t7)
/* 0000C8 004000C8 25EF000C */ addiu $t7, $t7, 0xc
/* 0000CC 004000CC 25CE000C */ addiu $t6, $t6, 0xc
/* 0000D0 004000D0 ADC1FFF4 */ sw $at, -0xc($t6)
/* 0000D4 004000D4 8DE1FFF8 */ lw $at, -8($t7)
/* 0000D8 004000D8 ADC1FFF8 */ sw $at, -8($t6)
/* 0000DC 004000DC 8DE1FFFC */ lw $at, -4($t7)
/* 0000E0 004000E0 15E8FFF8 */ bne $t7, $t0, .L004000C4
/* 0000E4 004000E4 ADC1FFFC */ sw $at, -4($t6)
/* 0000E8 004000E8 8DE10000 */ lw $at, ($t7)
/* 0000EC 004000EC 00A06025 */ move $t4, $a1
/* 0000F0 004000F0 00806825 */ move $t5, $a0
/* 0000F4 004000F4 24AB0060 */ addiu $t3, $a1, 0x60
/* 0000F8 004000F8 ADC10000 */ sw $at, ($t6)
.L004000FC:
/* 0000FC 004000FC 89810000 */ lwl $at, ($t4)
/* 000100 00400100 99810003 */ lwr $at, 3($t4)
/* 000104 00400104 258C000C */ addiu $t4, $t4, 0xc
/* 000108 00400108 25AD000C */ addiu $t5, $t5, 0xc
/* 00010C 0040010C A9A1FFF4 */ swl $at, -0xc($t5)
/* 000110 00400110 B9A1FFF7 */ swr $at, -9($t5)
/* 000114 00400114 8981FFF8 */ lwl $at, -8($t4)
/* 000118 00400118 9981FFFB */ lwr $at, -5($t4)
/* 00011C 0040011C A9A1FFF8 */ swl $at, -8($t5)
/* 000120 00400120 B9A1FFFB */ swr $at, -5($t5)
/* 000124 00400124 8981FFFC */ lwl $at, -4($t4)
/* 000128 00400128 9981FFFF */ lwr $at, -1($t4)
/* 00012C 0040012C A9A1FFFC */ swl $at, -4($t5)
/* 000130 00400130 158BFFF2 */ bne $t4, $t3, .L004000FC
/* 000134 00400134 B9A1FFFF */ swr $at, -1($t5)
/* 000138 00400138 89810000 */ lwl $at, ($t4)
/* 00013C 0040013C 99810003 */ lwr $at, 3($t4)
/* 000140 00400140 A9A10000 */ swl $at, ($t5)
/* 000144 00400144 03E00008 */ jr $ra
/* 000148 00400148 B9A10003 */ swr $at, 3($t5)
/* 00014C 0040014C 00000000 */ nop

View File

@@ -0,0 +1 @@
--target ppc-mwcc-c

View File

@@ -0,0 +1,41 @@
static ? a;
static ? b;
void test(s32 arg0, s32 arg1) {
s32 temp_r0;
s32 temp_r0_2;
s32 temp_r3;
s32 temp_r5;
s32 var_ctr;
s32 var_ctr_2;
void *var_r4;
void *var_r5;
void *var_r6;
void *var_r7;
var_r7 = &a - 4;
var_r6 = &b - 4;
var_ctr = 0x32;
do {
temp_r5 = var_r6->unk4;
temp_r0 = var_r6->unk8;
var_r6 += 8;
var_r7->unk4 = temp_r5;
var_r7 += 8;
var_r7->unk8 = temp_r0;
var_ctr -= 1;
} while (var_ctr != 0);
var_r5 = arg0 - 4;
var_r4 = arg1 - 4;
var_ctr_2 = 0xC;
do {
temp_r3 = var_r4->unk4;
temp_r0_2 = var_r4->unk8;
var_r4 += 8;
var_r5->unk4 = temp_r3;
var_r5 += 8;
var_r5->unk8 = temp_r0_2;
var_ctr_2 -= 1;
} while (var_ctr_2 != 0);
var_r5->unk4 = (s32) var_r4->unk4;
}

View File

@@ -0,0 +1,242 @@
.include "macros.inc"
.section .text # 0x0 - 0x64
.global test
test:
/* 00000000 00000000 3C C0 00 00 */ lis r6, a@ha
/* 00000004 00000004 3C A0 00 00 */ lis r5, b@ha
/* 00000008 00000008 38 C6 00 00 */ addi r6, r6, a@l
/* 0000000C 0000000C 38 00 00 32 */ li r0, 0x32
/* 00000010 00000010 38 A5 00 00 */ addi r5, r5, b@l
/* 00000014 00000014 38 E6 FF FC */ addi r7, r6, -4
/* 00000018 00000018 38 C5 FF FC */ addi r6, r5, -4
/* 0000001C 0000001C 7C 09 03 A6 */ mtctr r0
.L00000020:
/* 00000020 00000020 80 A6 00 04 */ lwz r5, 4(r6)
/* 00000024 00000024 84 06 00 08 */ lwzu r0, 8(r6)
/* 00000028 00000028 90 A7 00 04 */ stw r5, 4(r7)
/* 0000002C 0000002C 94 07 00 08 */ stwu r0, 8(r7)
/* 00000030 00000030 42 00 FF F0 */ bdnz .L00000020
/* 00000034 00000034 38 00 00 0C */ li r0, 0xc
/* 00000038 00000038 38 A3 FF FC */ addi r5, r3, -4
/* 0000003C 0000003C 38 84 FF FC */ addi r4, r4, -4
/* 00000040 00000040 7C 09 03 A6 */ mtctr r0
.L00000044:
/* 00000044 00000044 80 64 00 04 */ lwz r3, 4(r4)
/* 00000048 00000048 84 04 00 08 */ lwzu r0, 8(r4)
/* 0000004C 0000004C 90 65 00 04 */ stw r3, 4(r5)
/* 00000050 00000050 94 05 00 08 */ stwu r0, 8(r5)
/* 00000054 00000054 42 00 FF F0 */ bdnz .L00000044
/* 00000058 00000058 80 04 00 04 */ lwz r0, 4(r4)
/* 0000005C 0000005C 90 05 00 04 */ stw r0, 4(r5)
/* 00000060 00000060 4E 80 00 20 */ blr
.section .bss # 0x0 - 0x320
.global a
a:
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.global b
b:
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000

View File

@@ -0,0 +1,10 @@
struct A {
int buf[100];
} a, b;
struct B {
char buf[100];
};
void test(struct B *c, struct B *d) {
a = b;
*c = *d;
}

View File

@@ -59,6 +59,7 @@ if "source" in form:
"ppc-mwcc-c++",
"mips-ido-c",
"mips-gcc-c",
"mipsel-gcc-c",
):
cmd.extend(["--target", value])
if "nounkinference" in form:
@@ -236,6 +237,7 @@ label {
<select name="target">
<option value="mips-ido-c">MIPS, IDO, C</option>
<option value="mips-gcc-c">MIPS, GCC, C</option>
<option value="mipsel-gcc-c">MIPSEL, GCC, C</option>
<option value="ppc-mwcc-c++">PPC, MWCC, C++</option>
<option value="ppc-mwcc-c">PPC, MWCC, C</option>
</select>