mirror of
https://github.com/decompme/decomp.me.git
synced 2026-02-21 05:49:05 -06:00
git subrepo pull backend/mips_to_c
subrepo: subdir: "backend/mips_to_c" merged: "c0ddea7" upstream: origin: "https://github.com/matt-kempster/mips_to_c" branch: "master" commit: "c0ddea7" git-subrepo: version: "0.4.3" origin: "???" commit: "???"
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/matt-kempster/mips_to_c
|
||||
branch = master
|
||||
commit = 3c3b0cede1a99430bfd3edf8d385802b94f91307
|
||||
parent = be2d33a8d349224b72e85ab418eb2c21c2de18d9
|
||||
commit = c0ddea7fd365f56b531f3b981f0f678730f2b882
|
||||
parent = 7d76774b085f31411e8473b7c8844247e76fb6d1
|
||||
method = merge
|
||||
cmdver = 0.4.3
|
||||
|
||||
0
backend/mips_to_c/__init__.py
Normal file
0
backend/mips_to_c/__init__.py
Normal file
@@ -68,8 +68,10 @@ class TypeMap:
|
||||
typedefs: Dict[str, CType] = field(default_factory=dict)
|
||||
var_types: Dict[str, CType] = field(default_factory=dict)
|
||||
functions: Dict[str, Function] = field(default_factory=dict)
|
||||
structs: Dict[Union[str, int], Struct] = field(default_factory=dict)
|
||||
struct_typedefs: Dict[Union[str, int], CType] = field(default_factory=dict)
|
||||
structs: Dict[Union[str, StructUnion], Struct] = field(default_factory=dict)
|
||||
struct_typedefs: Dict[Union[str, StructUnion], TypeDecl] = field(
|
||||
default_factory=dict
|
||||
)
|
||||
enum_values: Dict[str, int] = field(default_factory=dict)
|
||||
|
||||
|
||||
@@ -97,20 +99,6 @@ def resolve_typedefs(type: CType, typemap: TypeMap) -> CType:
|
||||
return type
|
||||
|
||||
|
||||
def pointer_decay(type: CType, typemap: TypeMap) -> SimpleType:
|
||||
real_type = resolve_typedefs(type, typemap)
|
||||
if isinstance(real_type, ArrayDecl):
|
||||
return PtrDecl(quals=[], type=real_type.type)
|
||||
if isinstance(real_type, FuncDecl):
|
||||
return PtrDecl(quals=[], type=type)
|
||||
if isinstance(real_type, TypeDecl) and isinstance(real_type.type, ca.Enum):
|
||||
return basic_type(["int"])
|
||||
assert not isinstance(
|
||||
type, (ArrayDecl, FuncDecl)
|
||||
), "resolve_typedefs can't hide arrays/functions"
|
||||
return type
|
||||
|
||||
|
||||
def type_from_global_decl(decl: ca.Decl) -> CType:
|
||||
"""Get the CType of a global Decl, stripping names of function parameters."""
|
||||
tp = decl.type
|
||||
@@ -130,12 +118,6 @@ def type_from_global_decl(decl: ca.Decl) -> CType:
|
||||
return ca.FuncDecl(args=ca.ParamList(new_params), type=tp.type)
|
||||
|
||||
|
||||
def deref_type(type: CType, typemap: TypeMap) -> CType:
|
||||
type = resolve_typedefs(type, typemap)
|
||||
assert isinstance(type, (ArrayDecl, PtrDecl)), "dereferencing non-pointer"
|
||||
return type.type
|
||||
|
||||
|
||||
def is_void(type: CType) -> bool:
|
||||
return (
|
||||
isinstance(type, ca.TypeDecl)
|
||||
@@ -144,35 +126,6 @@ def is_void(type: CType) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def equal_types(a: CType, b: CType) -> bool:
|
||||
def equal(a: object, b: object) -> bool:
|
||||
if a is b:
|
||||
return True
|
||||
if type(a) != type(b):
|
||||
return False
|
||||
if a is None:
|
||||
return b is None
|
||||
if isinstance(a, list):
|
||||
assert isinstance(b, list)
|
||||
if len(a) != len(b):
|
||||
return False
|
||||
for i in range(len(a)):
|
||||
if not equal(a[i], b[i]):
|
||||
return False
|
||||
return True
|
||||
if isinstance(a, (int, str)):
|
||||
return bool(a == b)
|
||||
assert isinstance(a, ca.Node)
|
||||
for name in a.__slots__[:-2]: # type: ignore
|
||||
if name == "declname":
|
||||
continue
|
||||
if not equal(getattr(a, name), getattr(b, name)):
|
||||
return False
|
||||
return True
|
||||
|
||||
return equal(a, b)
|
||||
|
||||
|
||||
def primitive_size(type: Union[ca.Enum, ca.IdentifierType]) -> int:
|
||||
if isinstance(type, ca.Enum):
|
||||
return 4
|
||||
@@ -210,11 +163,6 @@ def function_arg_size_align(type: CType, typemap: TypeMap) -> Tuple[int, int]:
|
||||
return size, size
|
||||
|
||||
|
||||
def var_size_align(type: CType, typemap: TypeMap) -> Tuple[int, int]:
|
||||
size, align, _ = parse_struct_member(type, "", typemap, allow_unsized=True)
|
||||
return size, align
|
||||
|
||||
|
||||
def is_struct_type(type: CType, typemap: TypeMap) -> bool:
|
||||
type = resolve_typedefs(type, typemap)
|
||||
if not isinstance(type, TypeDecl):
|
||||
@@ -373,7 +321,7 @@ def get_struct(
|
||||
if struct.name:
|
||||
return typemap.structs.get(struct.name)
|
||||
else:
|
||||
return typemap.structs.get(id(struct))
|
||||
return typemap.structs.get(struct)
|
||||
|
||||
|
||||
def parse_struct(struct: Union[ca.Struct, ca.Union], typemap: TypeMap) -> Struct:
|
||||
@@ -385,7 +333,7 @@ def parse_struct(struct: Union[ca.Struct, ca.Union], typemap: TypeMap) -> Struct
|
||||
ret = do_parse_struct(struct, typemap)
|
||||
if struct.name:
|
||||
typemap.structs[struct.name] = ret
|
||||
typemap.structs[id(struct)] = ret
|
||||
typemap.structs[struct] = ret
|
||||
return ret
|
||||
|
||||
|
||||
@@ -420,22 +368,6 @@ def parse_struct_member(
|
||||
return size, size, None
|
||||
|
||||
|
||||
def expand_detailed_struct_member(
|
||||
substr: DetailedStructMember, type: CType, size: int
|
||||
) -> Iterator[Tuple[int, str, CType, int]]:
|
||||
yield (0, "", type, size)
|
||||
if isinstance(substr, Struct):
|
||||
for off, sfields in substr.fields.items():
|
||||
for field in sfields:
|
||||
yield (off, "." + field.name, field.type, field.size)
|
||||
elif isinstance(substr, Array) and substr.subsize != 1:
|
||||
for i in range(substr.dim):
|
||||
for (off, path, subtype, subsize) in expand_detailed_struct_member(
|
||||
substr.subtype, substr.subctype, substr.subsize
|
||||
):
|
||||
yield (substr.subsize * i + off, f"[{i}]" + path, subtype, subsize)
|
||||
|
||||
|
||||
def do_parse_struct(struct: Union[ca.Struct, ca.Union], typemap: TypeMap) -> Struct:
|
||||
is_union = isinstance(struct, ca.Union)
|
||||
assert struct.decls is not None, "enforced by caller"
|
||||
@@ -495,12 +427,13 @@ def do_parse_struct(struct: Union[ca.Struct, ca.Union], typemap: TypeMap) -> Str
|
||||
)
|
||||
align = max(align, salign)
|
||||
offset = (offset + salign - 1) & -salign
|
||||
for off, path, ftype, fsize in expand_detailed_struct_member(
|
||||
substr, type, ssize
|
||||
):
|
||||
fields[offset + off].append(
|
||||
StructField(type=ftype, size=fsize, name=decl.name + path)
|
||||
fields[offset].append(
|
||||
StructField(
|
||||
type=type,
|
||||
size=ssize,
|
||||
name=decl.name,
|
||||
)
|
||||
)
|
||||
if is_union:
|
||||
union_size = max(union_size, ssize)
|
||||
else:
|
||||
@@ -531,8 +464,8 @@ def do_parse_struct(struct: Union[ca.Struct, ca.Union], typemap: TypeMap) -> Str
|
||||
offset += 1
|
||||
|
||||
# If there is a typedef for this struct, prefer using that name
|
||||
if id(struct) in typemap.struct_typedefs:
|
||||
ctype = typemap.struct_typedefs[id(struct)]
|
||||
if struct in typemap.struct_typedefs:
|
||||
ctype = typemap.struct_typedefs[struct]
|
||||
elif struct.name and struct.name in typemap.struct_typedefs:
|
||||
ctype = typemap.struct_typedefs[struct.name]
|
||||
else:
|
||||
@@ -641,7 +574,7 @@ def build_typemap(source: str) -> TypeMap:
|
||||
typedef = basic_type([item.name])
|
||||
if item.type.type.name:
|
||||
ret.struct_typedefs[item.type.type.name] = typedef
|
||||
ret.struct_typedefs[id(item.type.type)] = typedef
|
||||
ret.struct_typedefs[item.type.type] = typedef
|
||||
if isinstance(item, ca.FuncDef):
|
||||
assert item.decl.name is not None, "cannot define anonymous function"
|
||||
fn = parse_function(item.decl.type)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import NoReturn
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -7,3 +8,7 @@ class DecompFailure(Exception):
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.message
|
||||
|
||||
|
||||
def static_assert_unreachable(x: NoReturn) -> NoReturn:
|
||||
raise Exception(f"Unreachable: {repr(x)}")
|
||||
|
||||
@@ -156,7 +156,7 @@ class LabelStatement:
|
||||
for (switch, case) in self.context.case_nodes[self.node]:
|
||||
if case is not None:
|
||||
case_num = f"0x{case:X}" if fmt.coding_style.hex_case else f"{case}"
|
||||
case_str = f"case {case_num}"
|
||||
case_str = f"case {case_num}"
|
||||
else:
|
||||
case_str = "default"
|
||||
switch_str = f" // switch {switch}" if switch != 0 else ""
|
||||
|
||||
@@ -41,7 +41,7 @@ def print_exception(sanitize: bool) -> None:
|
||||
def run(options: Options) -> int:
|
||||
all_functions: Dict[str, Function] = {}
|
||||
asm_data = AsmData()
|
||||
typemap: Optional[TypeMap] = None
|
||||
typemap: TypeMap = TypeMap()
|
||||
try:
|
||||
for filename in options.filenames:
|
||||
if filename == "-":
|
||||
@@ -63,7 +63,6 @@ def run(options: Options) -> int:
|
||||
return 1
|
||||
|
||||
if options.dump_typemap:
|
||||
assert typemap
|
||||
dump_typemap(typemap)
|
||||
return 0
|
||||
|
||||
@@ -88,7 +87,7 @@ def run(options: Options) -> int:
|
||||
functions.append(all_functions[index_or_name])
|
||||
|
||||
function_names = set(all_functions.keys())
|
||||
global_info = GlobalInfo(asm_data, function_names, typemap)
|
||||
global_info = GlobalInfo(asm_data, function_names, typemap=typemap)
|
||||
function_infos: List[Union[FunctionInfo, Exception]] = []
|
||||
for function in functions:
|
||||
try:
|
||||
|
||||
@@ -19,8 +19,8 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from .c_types import TypeMap
|
||||
from .error import DecompFailure
|
||||
from .c_types import CType, TypeMap
|
||||
from .error import DecompFailure, static_assert_unreachable
|
||||
from .flow_graph import (
|
||||
FlowGraph,
|
||||
Function,
|
||||
@@ -43,13 +43,12 @@ from .parse_instruction import (
|
||||
Register,
|
||||
)
|
||||
from .types import (
|
||||
AccessPath,
|
||||
FunctionParam,
|
||||
FunctionSignature,
|
||||
StructDeclaration,
|
||||
Type,
|
||||
find_substruct_array,
|
||||
get_field,
|
||||
ptr_type_from_ctype,
|
||||
type_from_ctype,
|
||||
TypePool,
|
||||
)
|
||||
|
||||
ASSOCIATIVE_OPS: Set[str] = {"+", "&&", "||", "&", "|", "^", "*"}
|
||||
@@ -808,6 +807,23 @@ class BinaryOp(Condition):
|
||||
return f"({lhs} {self.op} {right_expr.format(fmt)})"
|
||||
|
||||
|
||||
@dataclass(frozen=True, eq=False)
|
||||
class TernaryOp(Expression):
|
||||
cond: Condition
|
||||
left: Expression
|
||||
right: Expression
|
||||
type: Type
|
||||
|
||||
def dependencies(self) -> List[Expression]:
|
||||
return [self.cond, self.left, self.right]
|
||||
|
||||
def format(self, fmt: Formatter) -> str:
|
||||
cond_str = simplify_condition(self.cond).format(fmt)
|
||||
left_str = self.left.format(fmt)
|
||||
right_str = self.right.format(fmt)
|
||||
return f"({cond_str} ? {left_str} : {right_str})"
|
||||
|
||||
|
||||
@dataclass(frozen=True, eq=False)
|
||||
class UnaryOp(Condition):
|
||||
op: str
|
||||
@@ -1007,42 +1023,83 @@ class StructAccess(Expression):
|
||||
struct_var: Expression
|
||||
offset: int
|
||||
target_size: Optional[int]
|
||||
field_name: Optional[str] = field(compare=False)
|
||||
field_path: Optional[AccessPath] = field(compare=False)
|
||||
stack_info: StackInfo = field(compare=False, repr=False)
|
||||
type: Type = field(compare=False)
|
||||
has_late_field_name: bool = field(default=False, compare=False)
|
||||
checked_late_field_path: bool = field(default=False, compare=False)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.assert_valid_field_path(self.field_path)
|
||||
|
||||
@staticmethod
|
||||
def assert_valid_field_path(path: Optional[AccessPath]) -> None:
|
||||
assert path is None or (
|
||||
path and isinstance(path[0], int)
|
||||
), "The first element of the field path, if present, must be an int"
|
||||
|
||||
@classmethod
|
||||
def access_path_to_field_name(cls, path: AccessPath) -> str:
|
||||
"""
|
||||
Convert an access path into a dereferencing field name, like the following examples:
|
||||
- `[0, "foo", 3, "bar"]` into `"->foo[3].bar"`
|
||||
- `[0, 3, "bar"]` into `"[0][3].bar"`
|
||||
- `[0, 1, 2]` into `"[0][1][2]"
|
||||
- `[0]` into `"[0]"`
|
||||
The path must have at least one element, and the first element must be an int.
|
||||
"""
|
||||
cls.assert_valid_field_path(path)
|
||||
output = ""
|
||||
|
||||
# Replace an initial "[0]." with "->"
|
||||
if len(path) >= 2 and path[0] == 0 and isinstance(path[1], str):
|
||||
output += f"->{path[1]}"
|
||||
path = path[2:]
|
||||
|
||||
for p in path:
|
||||
if isinstance(p, str):
|
||||
output += f".{p}"
|
||||
elif isinstance(p, int):
|
||||
output += f"[{p}]"
|
||||
else:
|
||||
static_assert_unreachable(p)
|
||||
return output
|
||||
|
||||
def dependencies(self) -> List[Expression]:
|
||||
return [self.struct_var]
|
||||
|
||||
def late_field_name(self) -> Optional[str]:
|
||||
def make_reference(self) -> Optional["StructAccess"]:
|
||||
field_path = self.late_field_path()
|
||||
if field_path and len(field_path) >= 2 and field_path[-1] == 0:
|
||||
return replace(self, field_path=field_path[:-1])
|
||||
return None
|
||||
|
||||
def late_field_path(self) -> Optional[AccessPath]:
|
||||
# If we didn't have a type at the time when the struct access was
|
||||
# constructed, but now we do, compute field name.
|
||||
if (
|
||||
self.field_name is None
|
||||
and self.stack_info.global_info.typemap
|
||||
and not self.has_late_field_name
|
||||
):
|
||||
|
||||
if self.field_path is None and not self.checked_late_field_path:
|
||||
var = late_unwrap(self.struct_var)
|
||||
self.field_name = get_field(
|
||||
var.type,
|
||||
self.offset,
|
||||
target_size=self.target_size,
|
||||
)[0]
|
||||
self.has_late_field_name = True
|
||||
return self.field_name
|
||||
field_path, field_type, remaining_offset = var.type.get_deref_field(
|
||||
self.offset, target_size=self.target_size
|
||||
)
|
||||
if field_path is not None and remaining_offset == 0:
|
||||
self.assert_valid_field_path(field_path)
|
||||
self.field_path = field_path
|
||||
self.type.unify(field_type)
|
||||
|
||||
self.checked_late_field_path = True
|
||||
return self.field_path
|
||||
|
||||
def late_has_known_type(self) -> bool:
|
||||
if self.late_field_name() is not None:
|
||||
if self.late_field_path() is not None:
|
||||
return True
|
||||
if self.offset == 0 and self.stack_info.global_info.typemap:
|
||||
if self.offset == 0:
|
||||
var = late_unwrap(self.struct_var)
|
||||
if (
|
||||
not self.stack_info.has_nonzero_access(var)
|
||||
and isinstance(var, AddressOf)
|
||||
and isinstance(var.expr, GlobalSymbol)
|
||||
and var.expr.symbol_name
|
||||
in self.stack_info.global_info.typemap.var_types
|
||||
and var.expr.type_in_typemap
|
||||
):
|
||||
return True
|
||||
return False
|
||||
@@ -1051,9 +1108,9 @@ class StructAccess(Expression):
|
||||
var = late_unwrap(self.struct_var)
|
||||
has_nonzero_access = self.stack_info.has_nonzero_access(var)
|
||||
|
||||
field_name = self.late_field_name()
|
||||
field_path = self.late_field_path()
|
||||
|
||||
if field_name:
|
||||
if field_path is not None and field_path != [0]:
|
||||
has_nonzero_access = True
|
||||
elif fmt.valid_syntax and (self.offset != 0 or has_nonzero_access):
|
||||
offset_str = (
|
||||
@@ -1062,27 +1119,25 @@ class StructAccess(Expression):
|
||||
return f"MIPS2C_FIELD({var.format(fmt)}, {Type.ptr(self.type).format(fmt)}, {offset_str})"
|
||||
else:
|
||||
prefix = "unk" + ("_" if fmt.coding_style.unknown_underscore else "")
|
||||
field_name = prefix + format_hex(self.offset)
|
||||
field_path = [0, prefix + format_hex(self.offset)]
|
||||
field_name = self.access_path_to_field_name(field_path)
|
||||
|
||||
if isinstance(var, AddressOf):
|
||||
if isinstance(var.expr, GlobalSymbol) and var.expr.array_dim is not None:
|
||||
needs_deref = True
|
||||
else:
|
||||
needs_deref = False
|
||||
var = var.expr
|
||||
else:
|
||||
needs_deref = True
|
||||
# Rewrite `(&x)->y` to `x.y` by stripping `AddressOf` & setting deref=False
|
||||
deref = True
|
||||
if (
|
||||
isinstance(var, AddressOf)
|
||||
and not var.expr.type.is_array()
|
||||
and field_name.startswith("->")
|
||||
):
|
||||
var = var.expr
|
||||
field_name = field_name.replace("->", ".", 1)
|
||||
deref = False
|
||||
|
||||
if needs_deref:
|
||||
if self.offset == 0 and not has_nonzero_access:
|
||||
return f"*{var.format(fmt)}"
|
||||
else:
|
||||
return f"{parenthesize_for_struct_access(var, fmt)}->{field_name}"
|
||||
else:
|
||||
if self.offset == 0 and not has_nonzero_access:
|
||||
return f"{var.format(fmt)}"
|
||||
else:
|
||||
return f"{parenthesize_for_struct_access(var, fmt)}.{field_name}"
|
||||
# Rewrite `x->unk0` to `*x` and `x.unk0` to `x`, unless has_nonzero_access
|
||||
if self.offset == 0 and not has_nonzero_access:
|
||||
return f"{'*' if deref else ''}{var.format(fmt)}"
|
||||
|
||||
return f"{parenthesize_for_struct_access(var, fmt)}{field_name}"
|
||||
|
||||
|
||||
@dataclass(frozen=True, eq=True)
|
||||
@@ -1178,6 +1233,9 @@ class Literal(Expression):
|
||||
)
|
||||
return prefix + mid + suffix
|
||||
|
||||
def likely_partial_offset(self) -> bool:
|
||||
return self.value % 2 ** 15 in (0, 2 ** 15 - 1) and self.value < 0x1000000
|
||||
|
||||
|
||||
@dataclass(frozen=True, eq=True)
|
||||
class AddressOf(Expression):
|
||||
@@ -1191,13 +1249,17 @@ class AddressOf(Expression):
|
||||
if isinstance(self.expr, GlobalSymbol):
|
||||
if self.expr.is_string_constant():
|
||||
return self.expr.format_string_constant(fmt)
|
||||
if self.expr.array_dim is not None:
|
||||
return f"{self.expr.format(fmt)}"
|
||||
|
||||
if self.expr.type.is_array():
|
||||
return f"{self.expr.format(fmt)}"
|
||||
if self.expr.type.is_function():
|
||||
# Functions are automatically converted to function pointers
|
||||
# without an explicit `&` by the compiler
|
||||
return f"{self.expr.format(fmt)}"
|
||||
if isinstance(self.expr, StructAccess):
|
||||
# Simplify `&x[0]` into `x`
|
||||
ref = self.expr.make_reference()
|
||||
if ref:
|
||||
return f"{ref.format(fmt)}"
|
||||
return f"&{self.expr.format(fmt)}"
|
||||
|
||||
|
||||
@@ -1819,11 +1881,7 @@ def deref(
|
||||
uw_var = early_unwrap(var)
|
||||
if isinstance(uw_var, BinaryOp) and uw_var.op == "+":
|
||||
for base, addend in [(uw_var.left, uw_var.right), (uw_var.right, uw_var.left)]:
|
||||
if (
|
||||
isinstance(addend, Literal)
|
||||
and addend.value % 2 ** 15 in [0, 2 ** 15 - 1]
|
||||
and addend.value < 0x1000000
|
||||
):
|
||||
if isinstance(addend, Literal) and addend.likely_partial_offset():
|
||||
offset += addend.value
|
||||
var = base
|
||||
break
|
||||
@@ -1839,31 +1897,20 @@ def deref(
|
||||
)
|
||||
if array_expr is not None:
|
||||
return array_expr
|
||||
field_name, new_type, _, _ = get_field(var.type, offset, target_size=size)
|
||||
if field_name is not None:
|
||||
new_type.unify(type)
|
||||
type = new_type
|
||||
|
||||
# Dereferencing pointers of known types
|
||||
target = var.type.get_pointer_target()
|
||||
if field_name is None and target is not None:
|
||||
sub_size = target.get_size_bytes()
|
||||
if sub_size == size and offset % size == 0:
|
||||
# TODO: This only turns the deref into an ArrayAccess if the type
|
||||
# is *known* to be an array (CType). This could be expanded to support
|
||||
# arrays of other types.
|
||||
if offset != 0 and target.is_ctype():
|
||||
index = Literal(value=offset // size, type=Type.s32())
|
||||
return ArrayAccess(var, index, type=target)
|
||||
else:
|
||||
# Don't emit an array access, but at least help type inference along
|
||||
type = target
|
||||
field_path, field_type, remaining_offset = var.type.get_deref_field(
|
||||
offset, target_size=size
|
||||
)
|
||||
if field_path is not None and remaining_offset == 0:
|
||||
field_type.unify(type)
|
||||
type = field_type
|
||||
else:
|
||||
field_path = None
|
||||
|
||||
return StructAccess(
|
||||
struct_var=var,
|
||||
offset=offset,
|
||||
target_size=size,
|
||||
field_name=field_name,
|
||||
field_path=field_path,
|
||||
stack_info=stack_info,
|
||||
type=type,
|
||||
)
|
||||
@@ -2222,7 +2269,7 @@ def handle_addi_real(
|
||||
var = stack_info.get_stack_var(imm.value, store=False)
|
||||
if isinstance(var, LocalVar):
|
||||
stack_info.add_local_var(var)
|
||||
return AddressOf(var, type=Type.ptr(var.type))
|
||||
return AddressOf(var, type=var.type.reference())
|
||||
else:
|
||||
return add_imm(source, imm, stack_info)
|
||||
|
||||
@@ -2232,42 +2279,32 @@ def add_imm(source: Expression, imm: Expression, stack_info: StackInfo) -> Expre
|
||||
# addiu $reg1, $reg2, 0 is a move
|
||||
# (this happens when replacing %lo(...) by 0)
|
||||
return source
|
||||
elif source.type.is_pointer():
|
||||
elif source.type.is_pointer_or_array():
|
||||
# Pointer addition (this may miss some pointers that get detected later;
|
||||
# unfortunately that's hard to do anything about with mips_to_c's single-pass
|
||||
# architecture.
|
||||
if isinstance(imm, Literal):
|
||||
# architecture).
|
||||
if isinstance(imm, Literal) and not imm.likely_partial_offset():
|
||||
array_access = array_access_from_add(
|
||||
source, imm.value, stack_info, target_size=None, ptr=True
|
||||
)
|
||||
if array_access is not None:
|
||||
return array_access
|
||||
|
||||
field_name, subtype, ptr_type, array_dim = get_field(
|
||||
source.type, imm.value, target_size=None
|
||||
field_path, field_type, remaining_offset = source.type.get_deref_field(
|
||||
imm.value, target_size=None
|
||||
)
|
||||
if field_name is not None:
|
||||
if array_dim is not None:
|
||||
return StructAccess(
|
||||
if field_path is not None and remaining_offset == 0:
|
||||
return AddressOf(
|
||||
StructAccess(
|
||||
struct_var=source,
|
||||
offset=imm.value,
|
||||
target_size=None,
|
||||
field_name=field_name,
|
||||
field_path=field_path,
|
||||
stack_info=stack_info,
|
||||
type=ptr_type,
|
||||
)
|
||||
else:
|
||||
return AddressOf(
|
||||
StructAccess(
|
||||
struct_var=source,
|
||||
offset=imm.value,
|
||||
target_size=None,
|
||||
field_name=field_name,
|
||||
stack_info=stack_info,
|
||||
type=subtype,
|
||||
),
|
||||
type=ptr_type,
|
||||
)
|
||||
type=field_type,
|
||||
),
|
||||
type=field_type.reference(),
|
||||
)
|
||||
if isinstance(imm, Literal):
|
||||
target = source.type.get_pointer_target()
|
||||
if target:
|
||||
@@ -2336,6 +2373,9 @@ def deref_unaligned(
|
||||
|
||||
|
||||
def handle_lwl(args: InstrArgs) -> Expression:
|
||||
# Unaligned load for the left part of a register (lwl can technically merge with
|
||||
# a pre-existing lwr, but doesn't in practice, so we treat this as a standard
|
||||
# destination-first operation)
|
||||
ref = args.memory_ref(1)
|
||||
expr = deref_unaligned(ref, args.regs, args.stack_info)
|
||||
key: Tuple[int, object]
|
||||
@@ -2346,10 +2386,10 @@ def handle_lwl(args: InstrArgs) -> Expression:
|
||||
return Lwl(expr, key)
|
||||
|
||||
|
||||
def handle_lwr(args: InstrArgs, old_value: Expression) -> Expression:
|
||||
# This lwr may merge with an existing lwl, if it loads from the same target
|
||||
# but with an offset that's +3.
|
||||
uw_old_value = early_unwrap(old_value)
|
||||
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.
|
||||
uw_old_value = early_unwrap(args.reg(0))
|
||||
ref = args.memory_ref(1)
|
||||
lwl_key: Tuple[int, object]
|
||||
if isinstance(ref, AddressMode):
|
||||
@@ -2429,6 +2469,17 @@ def handle_sra(args: InstrArgs) -> Expression:
|
||||
return BinaryOp(as_s32(lhs), ">>", as_intish(shift), type=Type.s32())
|
||||
|
||||
|
||||
def handle_conditional_move(args: InstrArgs, nonzero: bool) -> Expression:
|
||||
op = "!=" if nonzero else "=="
|
||||
type = Type.any_reg()
|
||||
return TernaryOp(
|
||||
BinaryOp.scmp(args.reg(2), op, Literal(0)),
|
||||
as_type(args.reg(1), type, silent=True),
|
||||
as_type(args.reg(0), type, silent=True),
|
||||
type,
|
||||
)
|
||||
|
||||
|
||||
def format_f32_imm(num: int) -> str:
|
||||
packed = struct.pack(">I", num & (2 ** 32 - 1))
|
||||
value = struct.unpack(">f", packed)[0]
|
||||
@@ -2550,7 +2601,7 @@ def array_access_from_add(
|
||||
return None
|
||||
base = expr.left
|
||||
addend = expr.right
|
||||
if addend.type.is_pointer() and not base.type.is_pointer():
|
||||
if addend.type.is_pointer_or_array() and not base.type.is_pointer_or_array():
|
||||
base, addend = addend, base
|
||||
|
||||
index: Expression
|
||||
@@ -2583,38 +2634,48 @@ def array_access_from_add(
|
||||
pass
|
||||
else:
|
||||
# base->subarray[index]
|
||||
substr_array = find_substruct_array(base.type, offset, scale)
|
||||
if substr_array is None:
|
||||
sub_path, sub_type, remaining_offset = base.type.get_deref_field(
|
||||
offset, target_size=scale
|
||||
)
|
||||
# Check if the last item in the path is `0`, which indicates the start of an array
|
||||
# If it is, remove it: it will be replaced by `[index]`
|
||||
if sub_path is None or len(sub_path) < 2 or sub_path[-1] != 0:
|
||||
return None
|
||||
sub_field_name, sub_offset, elem_type = substr_array
|
||||
sub_path.pop()
|
||||
base = StructAccess(
|
||||
struct_var=base,
|
||||
offset=sub_offset,
|
||||
offset=offset - remaining_offset,
|
||||
target_size=None,
|
||||
field_name=sub_field_name,
|
||||
field_path=sub_path,
|
||||
stack_info=stack_info,
|
||||
type=Type.ptr(elem_type),
|
||||
type=sub_type,
|
||||
)
|
||||
offset -= sub_offset
|
||||
target_type = elem_type
|
||||
offset = remaining_offset
|
||||
target_type = sub_type
|
||||
|
||||
# Add .field if necessary
|
||||
ret: Expression = ArrayAccess(base, index, type=target_type)
|
||||
field_name, new_type, ptr_type, array_dim = get_field(
|
||||
base.type, offset, target_size=target_size
|
||||
|
||||
# Add .field if necessary by wrapping ret in StructAccess(AddressOf(...))
|
||||
ret_ref = AddressOf(ret, type=ret.type.reference())
|
||||
field_path, field_type, remaining_offset = ret_ref.type.get_deref_field(
|
||||
offset, target_size=target_size
|
||||
)
|
||||
if remaining_offset != 0:
|
||||
field_path = None
|
||||
|
||||
if offset != 0 or (target_size is not None and target_size != scale):
|
||||
ret = StructAccess(
|
||||
struct_var=AddressOf(ret, type=Type.ptr()),
|
||||
struct_var=ret_ref,
|
||||
offset=offset,
|
||||
target_size=target_size,
|
||||
field_name=field_name,
|
||||
field_path=field_path,
|
||||
stack_info=stack_info,
|
||||
type=ptr_type if array_dim is not None else new_type,
|
||||
type=field_type,
|
||||
)
|
||||
|
||||
if ptr and array_dim is None:
|
||||
ret = AddressOf(ret, type=ptr_type)
|
||||
if ptr:
|
||||
ret = AddressOf(ret, type=ret.type.reference())
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
@@ -2623,9 +2684,11 @@ def handle_add(args: InstrArgs) -> Expression:
|
||||
rhs = args.reg(2)
|
||||
stack_info = args.stack_info
|
||||
type = Type.intptr()
|
||||
if lhs.type.is_pointer():
|
||||
# Because lhs & rhs are in registers, it shouldn't be possible for them to be arrays.
|
||||
# If they are, treat them the same as pointers anyways.
|
||||
if lhs.type.is_pointer_or_array():
|
||||
type = Type.ptr()
|
||||
elif rhs.type.is_pointer():
|
||||
elif rhs.type.is_pointer_or_array():
|
||||
type = Type.ptr()
|
||||
|
||||
# addiu instructions can sometimes be emitted as addu instead, when the
|
||||
@@ -2726,7 +2789,7 @@ def function_abi(fn_sig: FunctionSignature, *, for_call: bool) -> Abi:
|
||||
only_floats = True
|
||||
slots: List[AbiArgSlot] = []
|
||||
possible: List[Register] = []
|
||||
if fn_sig.return_type.is_struct_type():
|
||||
if fn_sig.return_type.is_struct():
|
||||
# The ABI for struct returns is to pass a pointer to where it should be written
|
||||
# as the first argument.
|
||||
slots.append(
|
||||
@@ -2741,16 +2804,18 @@ def function_abi(fn_sig: FunctionSignature, *, for_call: bool) -> Abi:
|
||||
only_floats = False
|
||||
|
||||
for ind, param in enumerate(fn_sig.params):
|
||||
size, align = param.type.get_size_align_bytes()
|
||||
# Array parameters decay into pointers
|
||||
param_type = param.type.decay()
|
||||
size, align = param_type.get_parameter_size_align_bytes()
|
||||
size = (size + 3) & ~3
|
||||
only_floats = only_floats and param.type.is_float()
|
||||
only_floats = only_floats and param_type.is_float()
|
||||
offset = (offset + align - 1) & -align
|
||||
name = param.name
|
||||
reg2: Optional[Register]
|
||||
if ind < 2 and only_floats:
|
||||
reg = Register("f12" if ind == 0 else "f14")
|
||||
is_double = param.type.is_float() and param.type.get_size_bits() == 64
|
||||
slots.append(AbiArgSlot(offset=offset, reg=reg, name=name, type=param.type))
|
||||
is_double = param_type.is_float() and param_type.get_size_bits() == 64
|
||||
slots.append(AbiArgSlot(offset=offset, reg=reg, name=name, type=param_type))
|
||||
if is_double and not for_call:
|
||||
name2 = f"{name}_lo" if name else None
|
||||
reg2 = Register("f13" if ind == 0 else "f15")
|
||||
@@ -2765,7 +2830,7 @@ def function_abi(fn_sig: FunctionSignature, *, for_call: bool) -> Abi:
|
||||
name2 = f"{name}_unk{unk_offset:X}" if name and unk_offset else name
|
||||
reg2 = Register(f"a{i}") if i < 4 else None
|
||||
slots.append(
|
||||
AbiArgSlot(offset=4 * i, reg=reg2, name=name2, type=param.type)
|
||||
AbiArgSlot(offset=4 * i, reg=reg2, name=name2, type=param_type)
|
||||
)
|
||||
offset += size
|
||||
|
||||
@@ -2781,7 +2846,6 @@ def function_abi(fn_sig: FunctionSignature, *, for_call: bool) -> Abi:
|
||||
|
||||
InstrSet = Set[str]
|
||||
InstrMap = Dict[str, Callable[[InstrArgs], Expression]]
|
||||
LwrInstrMap = Dict[str, Callable[[InstrArgs, Expression], Expression]]
|
||||
CmpInstrMap = Dict[str, Callable[[InstrArgs], Condition]]
|
||||
StoreInstrMap = Dict[str, Callable[[InstrArgs], Optional[StoreStmt]]]
|
||||
MaybeInstrMap = Dict[str, Callable[[InstrArgs], Optional[Expression]]]
|
||||
@@ -3083,6 +3147,9 @@ CASES_DESTINATION_FIRST: InstrMap = {
|
||||
"mfc1": lambda a: a.reg(1),
|
||||
"mov.s": lambda a: a.reg(1),
|
||||
"mov.d": lambda a: as_f64(a.dreg(1)),
|
||||
# Conditional moves
|
||||
"movn": lambda a: handle_conditional_move(a, True),
|
||||
"movz": lambda a: handle_conditional_move(a, False),
|
||||
# FCSR get
|
||||
"cfc1": lambda a: ErrorExpr("cfc1"),
|
||||
# Immediates
|
||||
@@ -3095,23 +3162,18 @@ CASES_DESTINATION_FIRST: InstrMap = {
|
||||
"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)),
|
||||
"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)),
|
||||
"ldc1": lambda a: handle_load(a, type=Type.reg64(likely_float=True)),
|
||||
# Unaligned load for the left part of a register (lwl can technically merge
|
||||
# with a pre-existing lwr, but doesn't in practice, so we treat this as a
|
||||
# standard destination-first operation)
|
||||
# Unaligned loads
|
||||
"lwl": lambda a: handle_lwl(a),
|
||||
}
|
||||
CASES_LWR: LwrInstrMap = {
|
||||
# Unaligned load for the right part of a register. Only writes a partial
|
||||
# register.
|
||||
"lwr": lambda a, old_value: handle_lwr(a, old_value),
|
||||
"lwr": lambda a: handle_lwr(a),
|
||||
}
|
||||
|
||||
|
||||
def output_regs_for_instr(
|
||||
instr: Instruction, typemap: Optional[TypeMap]
|
||||
instr: Instruction, global_info: "GlobalInfo"
|
||||
) -> List[Register]:
|
||||
def reg_at(index: int) -> List[Register]:
|
||||
reg = instr.args[index]
|
||||
@@ -3130,11 +3192,10 @@ def output_regs_for_instr(
|
||||
or mnemonic in CASES_NO_DEST
|
||||
):
|
||||
return []
|
||||
if mnemonic == "jal" and typemap:
|
||||
if mnemonic == "jal":
|
||||
fn_target = instr.args[0]
|
||||
if isinstance(fn_target, AsmGlobalSymbol):
|
||||
c_fn = typemap.functions.get(fn_target.symbol_name)
|
||||
if c_fn and c_fn.ret_type is None:
|
||||
if global_info.is_function_known_void(fn_target.symbol_name):
|
||||
return []
|
||||
if mnemonic in CASES_FN_CALL:
|
||||
return list(map(Register, ["f0", "f1", "v0", "v1"]))
|
||||
@@ -3142,8 +3203,6 @@ def output_regs_for_instr(
|
||||
return reg_at(1)
|
||||
if mnemonic in CASES_DESTINATION_FIRST:
|
||||
return reg_at(0)
|
||||
if mnemonic in CASES_LWR:
|
||||
return reg_at(0)
|
||||
if mnemonic in CASES_FLOAT_COMP:
|
||||
return [Register("condition_bit")]
|
||||
if mnemonic in CASES_HI_LO:
|
||||
@@ -3154,7 +3213,7 @@ def output_regs_for_instr(
|
||||
|
||||
|
||||
def regs_clobbered_until_dominator(
|
||||
node: Node, typemap: Optional[TypeMap]
|
||||
node: Node, global_info: "GlobalInfo"
|
||||
) -> Set[Register]:
|
||||
if node.immediate_dominator is None:
|
||||
return set()
|
||||
@@ -3168,7 +3227,7 @@ def regs_clobbered_until_dominator(
|
||||
seen.add(n)
|
||||
for instr in n.block.instructions:
|
||||
with current_instr(instr):
|
||||
clobbered.update(output_regs_for_instr(instr, typemap))
|
||||
clobbered.update(output_regs_for_instr(instr, global_info))
|
||||
if instr.mnemonic in CASES_FN_CALL:
|
||||
clobbered.update(TEMP_REGS)
|
||||
stack.extend(n.parents)
|
||||
@@ -3176,7 +3235,7 @@ def regs_clobbered_until_dominator(
|
||||
|
||||
|
||||
def reg_always_set(
|
||||
node: Node, reg: Register, typemap: Optional[TypeMap], *, dom_set: bool
|
||||
node: Node, reg: Register, global_info: "GlobalInfo", *, dom_set: bool
|
||||
) -> bool:
|
||||
if node.immediate_dominator is None:
|
||||
return False
|
||||
@@ -3194,7 +3253,7 @@ def reg_always_set(
|
||||
with current_instr(instr):
|
||||
if instr.mnemonic in CASES_FN_CALL and reg in TEMP_REGS:
|
||||
clobbered = True
|
||||
if reg in output_regs_for_instr(instr, typemap):
|
||||
if reg in output_regs_for_instr(instr, global_info):
|
||||
clobbered = False
|
||||
if clobbered == True:
|
||||
return False
|
||||
@@ -3653,11 +3712,9 @@ def translate_node_body(node: Node, regs: RegInfo, stack_info: StackInfo) -> Blo
|
||||
if isinstance(fn_target, AddressOf) and isinstance(
|
||||
fn_target.expr, GlobalSymbol
|
||||
):
|
||||
typemap = stack_info.global_info.typemap
|
||||
if typemap:
|
||||
c_fn = typemap.functions.get(fn_target.expr.symbol_name)
|
||||
if c_fn and c_fn.ret_type is None:
|
||||
is_known_void = True
|
||||
is_known_void = stack_info.global_info.is_function_known_void(
|
||||
fn_target.expr.symbol_name
|
||||
)
|
||||
elif isinstance(fn_target, Literal):
|
||||
pass
|
||||
else:
|
||||
@@ -3838,13 +3895,6 @@ def translate_node_body(node: Node, regs: RegInfo, stack_info: StackInfo) -> Blo
|
||||
if (len(mn_parts) >= 2 and mn_parts[1] == "d") or mnemonic == "ldc1":
|
||||
set_reg(target.other_f64_reg(), SecondF64Half())
|
||||
|
||||
elif mnemonic in CASES_LWR:
|
||||
assert mnemonic == "lwr"
|
||||
target = args.reg_ref(0)
|
||||
old_value = args.reg(0)
|
||||
val = CASES_LWR[mnemonic](args, old_value)
|
||||
set_reg(target, val)
|
||||
|
||||
else:
|
||||
expr = ErrorExpr(f"unknown instruction: {instr}")
|
||||
if args.count() >= 1 and isinstance(args.raw_arg(0), Register):
|
||||
@@ -3941,7 +3991,6 @@ def translate_graph_from_block(
|
||||
|
||||
# Translate everything dominated by this node, now that we know our own
|
||||
# final register state. This will eventually reach every node.
|
||||
typemap = stack_info.global_info.typemap
|
||||
for child in node.immediately_dominates:
|
||||
if isinstance(child, TerminalNode):
|
||||
continue
|
||||
@@ -3949,9 +3998,11 @@ def translate_graph_from_block(
|
||||
for reg, data in regs.contents.items():
|
||||
new_regs.set_with_meta(reg, data.value, RegMeta(inherited=True))
|
||||
|
||||
phi_regs = regs_clobbered_until_dominator(child, typemap)
|
||||
phi_regs = regs_clobbered_until_dominator(child, stack_info.global_info)
|
||||
for reg in phi_regs:
|
||||
if reg_always_set(child, reg, typemap, dom_set=(reg in regs)):
|
||||
if reg_always_set(
|
||||
child, reg, stack_info.global_info, dom_set=(reg in regs)
|
||||
):
|
||||
expr: Optional[Expression] = stack_info.maybe_get_register_var(reg)
|
||||
if expr is None:
|
||||
expr = PhiExpr(
|
||||
@@ -3984,8 +4035,9 @@ def resolve_types_late(stack_info: StackInfo) -> None:
|
||||
class GlobalInfo:
|
||||
asm_data: AsmData
|
||||
local_functions: Set[str]
|
||||
typemap: Optional[TypeMap]
|
||||
global_symbol_map: Dict[str, GlobalSymbol] = field(default_factory=dict)
|
||||
typemap: TypeMap = field(default_factory=TypeMap)
|
||||
typepool: TypePool = field(default_factory=TypePool)
|
||||
|
||||
def asm_data_value(self, sym_name: str) -> Optional[AsmDataEntry]:
|
||||
return self.asm_data.values.get(sym_name)
|
||||
@@ -4000,16 +4052,24 @@ class GlobalInfo:
|
||||
asm_data_entry=self.asm_data_value(sym_name),
|
||||
)
|
||||
|
||||
type = Type.ptr(sym.type)
|
||||
if self.typemap:
|
||||
ctype = self.typemap.var_types.get(sym_name)
|
||||
if ctype:
|
||||
ctype_type, dim = ptr_type_from_ctype(ctype, self.typemap)
|
||||
sym.array_dim = dim
|
||||
fn = self.typemap.functions.get(sym_name)
|
||||
ctype: Optional[CType]
|
||||
if fn is not None:
|
||||
ctype = fn.type
|
||||
else:
|
||||
ctype = self.typemap.var_types.get(sym_name)
|
||||
if ctype is not None:
|
||||
sym.type_in_typemap = True
|
||||
type.unify(ctype_type)
|
||||
type = ctype_type
|
||||
return AddressOf(sym, type=type)
|
||||
sym.type.unify(Type.ctype(ctype, self.typemap, self.typepool))
|
||||
|
||||
return AddressOf(sym, type=sym.type.reference())
|
||||
|
||||
def is_function_known_void(self, sym_name: str) -> bool:
|
||||
"""Return True if the function exists in the context, and has no return value"""
|
||||
fn = self.typemap.functions.get(sym_name)
|
||||
if fn is None:
|
||||
return False
|
||||
return fn.ret_type is None
|
||||
|
||||
def initializer_for_symbol(
|
||||
self, sym: GlobalSymbol, fmt: Formatter
|
||||
@@ -4044,12 +4104,12 @@ class GlobalInfo:
|
||||
|
||||
def for_element_type(type: Type) -> Optional[str]:
|
||||
"""Return the initializer for a single element of type `type`"""
|
||||
if type.is_ctype():
|
||||
ctype_fields = type.get_ctype_fields()
|
||||
if not ctype_fields:
|
||||
if type.is_struct() or type.is_array():
|
||||
struct_fields = type.get_initializer_fields()
|
||||
if not struct_fields:
|
||||
return None
|
||||
members = []
|
||||
for field in ctype_fields:
|
||||
for field in struct_fields:
|
||||
if isinstance(field, int):
|
||||
# Check that all padding bytes are 0
|
||||
padding = read_uint(field)
|
||||
@@ -4211,7 +4271,6 @@ class GlobalInfo:
|
||||
continue
|
||||
|
||||
qualifier = f"{qualifier} " if qualifier else ""
|
||||
name = f"{name}[{sym.array_dim}]" if sym.array_dim is not None else name
|
||||
value = f" = {value}" if value else ""
|
||||
comment = f" // {'; '.join(comments)}" if comments else ""
|
||||
lines.append(
|
||||
@@ -4245,7 +4304,6 @@ def translate_to_ast(
|
||||
flow_graph: FlowGraph = build_flowgraph(function, global_info.asm_data)
|
||||
stack_info = get_stack_info(function, global_info, flow_graph)
|
||||
start_regs: RegInfo = RegInfo(stack_info=stack_info)
|
||||
typemap = global_info.typemap
|
||||
|
||||
start_regs[Register("sp")] = GlobalSymbol("sp", type=Type.ptr())
|
||||
for reg in SAVED_REGS:
|
||||
@@ -4255,14 +4313,11 @@ def translate_to_ast(
|
||||
assert offset % 4 == 0
|
||||
return PassedInArg(offset, copied=False, stack_info=stack_info, type=type)
|
||||
|
||||
if typemap and function.name in typemap.functions:
|
||||
fn_type = type_from_ctype(typemap.functions[function.name].type, typemap)
|
||||
fn_decl_provided = True
|
||||
else:
|
||||
fn_type = Type.function()
|
||||
fn_decl_provided = False
|
||||
fn_type.unify(global_info.address_of_gsym(function.name).expr.type)
|
||||
fn_sym = global_info.address_of_gsym(function.name).expr
|
||||
assert isinstance(fn_sym, GlobalSymbol)
|
||||
|
||||
fn_type = fn_sym.type
|
||||
fn_type.unify(Type.function())
|
||||
fn_sig = Type.ptr(fn_type).get_function_pointer_signature()
|
||||
assert fn_sig is not None, "fn_type is known to be a function"
|
||||
return_type = fn_sig.return_type
|
||||
@@ -4317,7 +4372,7 @@ def translate_to_ast(
|
||||
return_reg: Optional[Register] = None
|
||||
|
||||
if not options.void and not return_type.is_void():
|
||||
return_reg = determine_return_register(return_blocks, fn_decl_provided)
|
||||
return_reg = determine_return_register(return_blocks, fn_sym.type_in_typemap)
|
||||
|
||||
if return_reg is not None:
|
||||
for b in return_blocks:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,6 @@ extern s32 D_4100F0;
|
||||
extern f32 D_4100F4;
|
||||
|
||||
void test(f32 arg0, s32 arg1, f32 arg2, s32 arg3, f32 arg4, s32 arg5) {
|
||||
D_4100F4 = (f32) (arg0 + arg2 + arg4);
|
||||
D_4100F0 = (s32) (arg1 + arg3 + arg5);
|
||||
D_4100F4 = arg0 + arg2 + arg4;
|
||||
D_4100F0 = arg1 + arg3 + arg5;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ extern s32 D_4100E0;
|
||||
extern f32 D_4100E4;
|
||||
|
||||
void test(f32 arg0, s32 arg1, f32 arg2, s32 arg3, f32 arg4, s32 arg5) {
|
||||
D_4100E4 = (f32) (arg0 + arg2 + arg4);
|
||||
D_4100E0 = (s32) (arg1 + arg3 + arg5);
|
||||
D_4100E4 = arg0 + arg2 + arg4;
|
||||
D_4100E0 = arg1 + arg3 + arg5;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
extern s32 *D_410140;
|
||||
|
||||
void test(struct A *a, s32 b) {
|
||||
D_410140 = (s32 *) a->array[b];
|
||||
D_410140 = a->array[b];
|
||||
D_410140 = (s32 *) &a->array[b];
|
||||
D_410140 = (s32 *) a->array2[b].x;
|
||||
D_410140 = &a->array2[b].x;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
extern s32 *D_410110;
|
||||
|
||||
void test(struct A *a, s32 b) {
|
||||
D_410110 = (s32 *) a->array[b];
|
||||
D_410110 = a->array[b];
|
||||
D_410110 = (s32 *) &a->array[b];
|
||||
D_410110 = (s32 *) a->array2[b].x;
|
||||
D_410110 = &a->array2[b].x;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
extern s32 D_410100;
|
||||
|
||||
void test(s32 arg0, s32 arg1, s32 arg2) {
|
||||
D_410100 = (s32) (arg0 == arg1);
|
||||
D_410100 = (s32) (arg0 != arg2);
|
||||
D_410100 = (s32) (arg0 < arg1);
|
||||
D_410100 = (s32) ((arg1 < arg0) ^ 1);
|
||||
D_410100 = (s32) (arg0 == 0);
|
||||
D_410100 = (s32) (arg1 != 0);
|
||||
D_410100 = arg0 == arg1;
|
||||
D_410100 = arg0 != arg2;
|
||||
D_410100 = arg0 < arg1;
|
||||
D_410100 = (arg1 < arg0) ^ 1;
|
||||
D_410100 = arg0 == 0;
|
||||
D_410100 = arg1 != 0;
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ SomeStruct: size 0x280, align 8
|
||||
0x0: int_field (int)
|
||||
0x4: float_field (float)
|
||||
0x8: pointer_field (void *)
|
||||
0x10: data_field (union SomeUnion) data_field.double_innerfield (double) data_field.char_innerfield (char)
|
||||
0x10: data_field (union SomeUnion)
|
||||
0x18: enum_field (enum SomeEnum)
|
||||
0x1c: anon_enum_field (anon enum)
|
||||
0x20: anon_struct_field (anon struct) anon_struct_field.sub (int)
|
||||
0x20: anon_struct_field (anon struct)
|
||||
0x24: anon_union_field1 (int) anon_union_field2 (float)
|
||||
0x28: inner_struct_field (struct SubStruct) inner_struct_field.x (int)
|
||||
0x28: inner_struct_field (struct SubStruct)
|
||||
0x30: long_long_field (long long)
|
||||
0x38: bitfield_field (struct SomeBitfield)
|
||||
0x40: array_arithmetic_1 (char [1 + 1])
|
||||
@@ -63,8 +63,7 @@ SomeStruct: size 0x280, align 8
|
||||
0x20d: array_arithmetic_34 (char [16 + ((0) ? (2) : (3))])
|
||||
0x220: array_arithmetic_35 (char [16 + ((2, 3))])
|
||||
0x233: char_array (char [2])
|
||||
0x238: int_array (int [2]) int_array[0] (int)
|
||||
0x23c: int_array[1] (int)
|
||||
0x238: int_array (int [2])
|
||||
0x240: sub_array (struct
|
||||
{
|
||||
int a;
|
||||
@@ -72,26 +71,8 @@ SomeStruct: size 0x280, align 8
|
||||
{
|
||||
int c;
|
||||
} b[3];
|
||||
} [2]) sub_array[0] (anon struct) sub_array[0].a (int)
|
||||
0x244: sub_array[0].b (struct
|
||||
{
|
||||
int c;
|
||||
} [3]) sub_array[0].b[0] (anon struct) sub_array[0].b[0].c (int)
|
||||
0x248: sub_array[0].b[1] (anon struct) sub_array[0].b[1].c (int)
|
||||
0x24c: sub_array[0].b[2] (anon struct) sub_array[0].b[2].c (int)
|
||||
0x250: sub_array[1] (anon struct) sub_array[1].a (int)
|
||||
0x254: sub_array[1].b (struct
|
||||
{
|
||||
int c;
|
||||
} [3]) sub_array[1].b[0] (anon struct) sub_array[1].b[0].c (int)
|
||||
0x258: sub_array[1].b[1] (anon struct) sub_array[1].b[1].c (int)
|
||||
0x25c: sub_array[1].b[2] (anon struct) sub_array[1].b[2].c (int)
|
||||
0x260: multidim_array (int [2][3]) multidim_array[0] (int [3]) multidim_array[0][0] (int)
|
||||
0x264: multidim_array[0][1] (int)
|
||||
0x268: multidim_array[0][2] (int)
|
||||
0x26c: multidim_array[1] (int [3]) multidim_array[1][0] (int)
|
||||
0x270: multidim_array[1][1] (int)
|
||||
0x274: multidim_array[1][2] (int)
|
||||
} [2])
|
||||
0x260: multidim_array (int [2][3])
|
||||
0x278: end (char)
|
||||
|
||||
Enums:
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
s32 test(s32 arg2) {
|
||||
s32 temp_a2;
|
||||
|
||||
temp_a2 = (arg2 <= 0) ? 1 : arg2;
|
||||
return (temp_a2 < 6) ? temp_a2 : 5;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
glabel test
|
||||
li $a1,1
|
||||
slt $a0,$zero,$a2
|
||||
movz $a2,$a1,$a0
|
||||
li $v0,5
|
||||
slti $v1,$a2,6
|
||||
jr $ra
|
||||
movn $v0,$a2,$v1
|
||||
@@ -1,3 +1,3 @@
|
||||
void test(s32 *arg0) {
|
||||
*arg0 = (s32) ((s32) *arg0 / 2);
|
||||
*arg0 = (s32) *arg0 / 2;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
void test(s32 *arg0) {
|
||||
*arg0 = (s32) ((s32) *arg0 / 2);
|
||||
*arg0 = (s32) *arg0 / 2;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
MIPS2C_TRAP_IF((u32) arg0 >= 6U);
|
||||
MIPS2C_ERROR(unknown instruction: badinstr $t0, $t0);
|
||||
temp_t1 = MIPS2C_ERROR(unknown instruction: badinstr2 $t1, $t1);
|
||||
*NULL = (s32) (temp_t1 << temp_t1);
|
||||
*NULL = temp_t1 << temp_t1;
|
||||
*NULL = (s32) (MIPS2C_ERROR(Read from unset register $v1) + 2);
|
||||
return MIPS2C_ERROR(unknown instruction: badinstr3 $v0, $t2);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
s32 bar(f32); // extern
|
||||
s32 bar(f32 x); // extern
|
||||
extern s32 (*glob2)(f32);
|
||||
|
||||
void test(void) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
s32 test(void) {
|
||||
static_int *= 0x1C8;
|
||||
extern_float = (f32) (extern_float * 456.0f);
|
||||
extern_float *= 456.0f;
|
||||
static_fn(&static_A);
|
||||
extern_fn(static_A_ptr);
|
||||
*static_bss_array = *static_array + *static_ro_array;
|
||||
|
||||
@@ -20,7 +20,7 @@ s32 static_ro_array[3] = {7, 8, 9}; // const
|
||||
|
||||
s32 test(void) {
|
||||
static_int *= 0x1C8;
|
||||
extern_float = (f32) (extern_float * 456.0f);
|
||||
extern_float *= 456.0f;
|
||||
static_fn(&static_A);
|
||||
extern_fn(static_A_ptr);
|
||||
*static_bss_array = *static_array + *static_ro_array;
|
||||
|
||||
@@ -4,7 +4,7 @@ extern s32 D_4100F0;
|
||||
s32 temp_t6;
|
||||
|
||||
temp_t6 = D_4100F0;
|
||||
D_4100F0 = (s32) (temp_t6 - 1);
|
||||
D_4100F0 = temp_t6 - 1;
|
||||
if (temp_t6 < 1) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ extern s32 D_4100E0;
|
||||
s32 temp_v1;
|
||||
|
||||
temp_v1 = D_4100E0;
|
||||
D_4100E0 = (s32) (temp_v1 - 1);
|
||||
D_4100E0 = temp_v1 - 1;
|
||||
if (temp_v1 <= 0) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extern s32 D_4100F0;
|
||||
|
||||
void *test(void *arg0) {
|
||||
D_4100F0 = (s32) arg0->unk12348;
|
||||
D_4100F0 = arg0->unk12348;
|
||||
return arg0 + 0x12348;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extern s32 D_4100E0;
|
||||
|
||||
void *test(void *arg0) {
|
||||
D_4100E0 = (s32) arg0->unk12348;
|
||||
D_4100E0 = arg0->unk12348;
|
||||
return arg0 + 0x12348;
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@ void test(void) {
|
||||
D_410181 = (unaligned s32) D_410189;
|
||||
D_410190.unk0 = (unaligned s32) D_410180.unk0;
|
||||
D_410190.unk4 = (u8) D_410180.unk4;
|
||||
D_410198 = (s32) (unaligned s32) D_400178;
|
||||
D_410198 = (unaligned s32) D_400178;
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@ void test(void) {
|
||||
D_410161 = (unaligned s32) D_410169;
|
||||
D_410170.unk0 = (unaligned s32) D_410160.unk0;
|
||||
D_410170.unk4 = (u8) D_410160.unk4;
|
||||
D_410178 = (s32) (unaligned s32) D_400158;
|
||||
D_410178 = (unaligned s32) D_400158;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
void test(s32 *arg0) {
|
||||
*arg0 = (s32) ((s32) *arg0 % 2);
|
||||
*arg0 = (s32) *arg0 % 2;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
void test(s32 *arg0) {
|
||||
*arg0 = (s32) ((s32) *arg0 % 2);
|
||||
*arg0 = (s32) *arg0 % 2;
|
||||
}
|
||||
|
||||
@@ -2,25 +2,25 @@ extern s32 D_410250;
|
||||
|
||||
void test(s32 arg0) {
|
||||
D_410250 = arg0;
|
||||
D_410250 = (s32) (arg0 * 2);
|
||||
D_410250 = (s32) (arg0 * 3);
|
||||
D_410250 = (s32) (arg0 * 4);
|
||||
D_410250 = (s32) (arg0 * 5);
|
||||
D_410250 = (s32) (arg0 * 6);
|
||||
D_410250 = (s32) (arg0 * 7);
|
||||
D_410250 = (s32) (arg0 * 8);
|
||||
D_410250 = (s32) (arg0 * 9);
|
||||
D_410250 = (s32) (arg0 * 0xA);
|
||||
D_410250 = (s32) (arg0 * 0xB);
|
||||
D_410250 = (s32) (arg0 * 0xC);
|
||||
D_410250 = (s32) (arg0 * 0xD);
|
||||
D_410250 = (s32) (arg0 * 0xE);
|
||||
D_410250 = (s32) (arg0 * 0xF);
|
||||
D_410250 = (s32) (arg0 * 0x10);
|
||||
D_410250 = (s32) (arg0 * 0x11);
|
||||
D_410250 = (s32) (arg0 * 0x12);
|
||||
D_410250 = (s32) (arg0 * 0x13);
|
||||
D_410250 = (s32) (arg0 * 0x14);
|
||||
D_410250 = (s32) (arg0 * 0x15);
|
||||
D_410250 = (s32) (arg0 * 0x16);
|
||||
D_410250 = arg0 * 2;
|
||||
D_410250 = arg0 * 3;
|
||||
D_410250 = arg0 * 4;
|
||||
D_410250 = arg0 * 5;
|
||||
D_410250 = arg0 * 6;
|
||||
D_410250 = arg0 * 7;
|
||||
D_410250 = arg0 * 8;
|
||||
D_410250 = arg0 * 9;
|
||||
D_410250 = arg0 * 0xA;
|
||||
D_410250 = arg0 * 0xB;
|
||||
D_410250 = arg0 * 0xC;
|
||||
D_410250 = arg0 * 0xD;
|
||||
D_410250 = arg0 * 0xE;
|
||||
D_410250 = arg0 * 0xF;
|
||||
D_410250 = arg0 * 0x10;
|
||||
D_410250 = arg0 * 0x11;
|
||||
D_410250 = arg0 * 0x12;
|
||||
D_410250 = arg0 * 0x13;
|
||||
D_410250 = arg0 * 0x14;
|
||||
D_410250 = arg0 * 0x15;
|
||||
D_410250 = arg0 * 0x16;
|
||||
}
|
||||
|
||||
@@ -2,25 +2,25 @@ extern s32 D_4101F0;
|
||||
|
||||
void test(s32 arg0) {
|
||||
D_4101F0 = arg0;
|
||||
D_4101F0 = (s32) (arg0 * 2);
|
||||
D_4101F0 = (s32) (arg0 * 3);
|
||||
D_4101F0 = (s32) (arg0 * 4);
|
||||
D_4101F0 = (s32) (arg0 * 5);
|
||||
D_4101F0 = (s32) (arg0 * 6);
|
||||
D_4101F0 = (s32) (arg0 * 7);
|
||||
D_4101F0 = (s32) (arg0 * 8);
|
||||
D_4101F0 = (s32) (arg0 * 9);
|
||||
D_4101F0 = (s32) (arg0 * 0xA);
|
||||
D_4101F0 = (s32) (arg0 * 0xB);
|
||||
D_4101F0 = (s32) (arg0 * 0xC);
|
||||
D_4101F0 = (s32) (arg0 * 0xD);
|
||||
D_4101F0 = (s32) (arg0 * 0xE);
|
||||
D_4101F0 = (s32) (arg0 * 0xF);
|
||||
D_4101F0 = (s32) (arg0 * 0x10);
|
||||
D_4101F0 = (s32) (arg0 * 0x11);
|
||||
D_4101F0 = (s32) (arg0 * 0x12);
|
||||
D_4101F0 = (s32) (arg0 * 0x13);
|
||||
D_4101F0 = (s32) (arg0 * 0x14);
|
||||
D_4101F0 = (s32) (arg0 * 0x15);
|
||||
D_4101F0 = (s32) (arg0 * 0x16);
|
||||
D_4101F0 = arg0 * 2;
|
||||
D_4101F0 = arg0 * 3;
|
||||
D_4101F0 = arg0 * 4;
|
||||
D_4101F0 = arg0 * 5;
|
||||
D_4101F0 = arg0 * 6;
|
||||
D_4101F0 = arg0 * 7;
|
||||
D_4101F0 = arg0 * 8;
|
||||
D_4101F0 = arg0 * 9;
|
||||
D_4101F0 = arg0 * 0xA;
|
||||
D_4101F0 = arg0 * 0xB;
|
||||
D_4101F0 = arg0 * 0xC;
|
||||
D_4101F0 = arg0 * 0xD;
|
||||
D_4101F0 = arg0 * 0xE;
|
||||
D_4101F0 = arg0 * 0xF;
|
||||
D_4101F0 = arg0 * 0x10;
|
||||
D_4101F0 = arg0 * 0x11;
|
||||
D_4101F0 = arg0 * 0x12;
|
||||
D_4101F0 = arg0 * 0x13;
|
||||
D_4101F0 = arg0 * 0x14;
|
||||
D_4101F0 = arg0 * 0x15;
|
||||
D_4101F0 = arg0 * 0x16;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ extern f32 D_4100E0;
|
||||
extern f64 D_4100E8;
|
||||
|
||||
void test(void) {
|
||||
D_4100E0 = (f32) (2.0f * D_4100E0);
|
||||
D_4100E8 = (f64) (2.0 * D_4100E8);
|
||||
D_4100E0 *= 2.0f;
|
||||
D_4100E8 *= 2.0;
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ void test(s32 *arg0, s32 *arg1) {
|
||||
}
|
||||
return;
|
||||
block_3:
|
||||
*arg1 = (s32) (*arg1 + temp_v1);
|
||||
*arg1 += temp_v1;
|
||||
return;
|
||||
block_4:
|
||||
*arg1 = (s32) (*arg1 - temp_v1);
|
||||
*arg1 -= temp_v1;
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user