Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 54 additions & 8 deletions src/decompiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -226,16 +226,62 @@ struct Decompiler {

void LoadStore(Value &val, const Node& addr_exp, uint32_t offset,
Opcode opc, Address align, Type op_type) {
BracketIfNeeded(val, Precedence::Indexing);
auto access = lst.GenAccess(offset, addr_exp);
if (!access.empty()) {
// We can do this load/store as a struct access.
BracketIfNeeded(val, Precedence::Indexing);
val.v.back() += "." + access;
return;
}
// Do the load/store as a generalized indexing operation.
// The offset is divisible by the alignment in 99.99% of
// cases, but the spec doesn't guarantee it, so we must
// have a backup syntax.
auto index = offset % align == 0
? std::to_string(offset / align)
: cat(std::to_string(offset), "@", std::to_string(align));
// Detect the very common case of (base + (index << 2))[0]:int etc.
// so we can instead do base[index]:int
// TODO: (index << 2) on the left of + occurs also.
// TODO: sadly this does not address cases where the shift amount > align.
// (which happens for arrays of structs or arrays of long (with align=4)).
// TODO: also very common is (v = base + (index << 2))[0]:int
if (addr_exp.etype == ExprType::Binary) {
auto& pe = *cast<BinaryExpr>(addr_exp.e);
auto& shift_exp = addr_exp.children[1];
if (pe.opcode == Opcode::I32Add &&
shift_exp.etype == ExprType::Binary) {
auto& se = *cast<BinaryExpr>(shift_exp.e);
auto& const_exp = shift_exp.children[1];
if (se.opcode == Opcode::I32Shl &&
const_exp.etype == ExprType::Const) {
auto& ce = *cast<ConstExpr>(const_exp.e);
if (ce.const_.type == Type::I32 && (1 << ce.const_.u32) == align) {
// Pfew, case detected :( Lets re-write this in Haskell.
// TODO: we're decompiling these twice.
// The thing to the left of << is going to be part of the index.
auto ival = DecompileExpr(shift_exp.children[0], &shift_exp);
if (ival.v.size() == 1) { // Don't bother if huge.
if (offset == 0) {
index = ival.v[0];
} else {
BracketIfNeeded(ival, Precedence::Add);
index = cat(ival.v[0], " + ", index);
}
// We're going to use the thing to the left of + as the new
// base address:
val = DecompileExpr(addr_exp.children[0], &addr_exp);
}
}
}
}
}
BracketIfNeeded(val, Precedence::Indexing);
val.v.back() +=
!access.empty()
? "." + access
: cat("[", std::to_string(offset),
"]:", GetDecompTypeName(GetMemoryType(op_type, opc)),
!opc.IsNaturallyAligned(align)
? cat("@" + std::to_string(align))
: "");
cat("[", index, "]:", GetDecompTypeName(GetMemoryType(op_type, opc)),
opc.IsNaturallyAligned(align)
? ""
: cat("@", std::to_string(align)));
}

Value DecompileExpr(const Node& n, const Node* parent) {
Expand Down
4 changes: 2 additions & 2 deletions test/decompile/basic.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
if
i32.const 1
i32.const 2
i32.load offset=3
i32.load offset=3 align=1
i32.const 5
i32.add
i32.store offset=4
Expand Down Expand Up @@ -126,7 +126,7 @@ export function f(a:int, b:int):int {
var c:long = 8L;
var d:float = 6.0f;
var e:double = 7.0;
if (e < 10.0) { 1[4]:int = 2[3]:int + 5 }
if (e < 10.0) { 1[1]:int = 2[3]:int@1 + 5 }
f(a + g_b, 9);
loop L_b {
if (if (0) { 1 } else { 2 }) goto B_c;
Expand Down
47 changes: 45 additions & 2 deletions test/decompile/loadstore.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,48 @@
drop
;; 4) Unaligned access / access with unexpected gaps.
get_local 7
f32.load offset=1
f32.load offset=1 align=1
drop
;; Test index rewriting.
;; code that does (base + (index << 2))[0]:int is super common.
get_local 0
get_local 1
i32.const 2
i32.shl
i32.add
get_local 0
get_local 1
i32.const 2
i32.shl
i32.add
i32.load offset=0
i32.store offset=0
;; Same with non-zero offsets.
get_local 0
get_local 1
i32.const 2
i32.shl
i32.add
get_local 0
get_local 1
i32.const 2
i32.shl
i32.add
i32.load offset=4
i32.store offset=4
;; If the shift amount does not match, it doesn't work.
get_local 0
get_local 1
i32.const 3
i32.shl
i32.add
get_local 0
get_local 1
i32.const 2
i32.shl
i32.add
i32.load offset=4
i32.store offset=4
)
(export "f" (func $f))
)
Expand All @@ -77,7 +117,10 @@ export function f(a:{ a:float, b:float }, b:{ a:ushort, b:long }) {
f[0]:short;
g[0]:int;
g[0]:int@1;
h[1]:float;
h[1]:float@1;
a[b]:int = a[b]:int;
a[b + 1]:int = a[b + 1]:int;
(a + (b << 3))[1]:int = a[b + 1]:int;
}

;;; STDOUT ;;)