Skip to content

Commit 7de6d8e

Browse files
authored
wasm-decompile: friendlier general load/store ops. (#1284)
- Now has an index that is relative to the type. - Now detects the common case where the index is shifted to produce a new base address.
1 parent 5b2f61e commit 7de6d8e

File tree

3 files changed

+101
-12
lines changed

3 files changed

+101
-12
lines changed

src/decompiler.cc

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,16 +226,62 @@ struct Decompiler {
226226

227227
void LoadStore(Value &val, const Node& addr_exp, uint32_t offset,
228228
Opcode opc, Address align, Type op_type) {
229-
BracketIfNeeded(val, Precedence::Indexing);
230229
auto access = lst.GenAccess(offset, addr_exp);
230+
if (!access.empty()) {
231+
// We can do this load/store as a struct access.
232+
BracketIfNeeded(val, Precedence::Indexing);
233+
val.v.back() += "." + access;
234+
return;
235+
}
236+
// Do the load/store as a generalized indexing operation.
237+
// The offset is divisible by the alignment in 99.99% of
238+
// cases, but the spec doesn't guarantee it, so we must
239+
// have a backup syntax.
240+
auto index = offset % align == 0
241+
? std::to_string(offset / align)
242+
: cat(std::to_string(offset), "@", std::to_string(align));
243+
// Detect the very common case of (base + (index << 2))[0]:int etc.
244+
// so we can instead do base[index]:int
245+
// TODO: (index << 2) on the left of + occurs also.
246+
// TODO: sadly this does not address cases where the shift amount > align.
247+
// (which happens for arrays of structs or arrays of long (with align=4)).
248+
// TODO: also very common is (v = base + (index << 2))[0]:int
249+
if (addr_exp.etype == ExprType::Binary) {
250+
auto& pe = *cast<BinaryExpr>(addr_exp.e);
251+
auto& shift_exp = addr_exp.children[1];
252+
if (pe.opcode == Opcode::I32Add &&
253+
shift_exp.etype == ExprType::Binary) {
254+
auto& se = *cast<BinaryExpr>(shift_exp.e);
255+
auto& const_exp = shift_exp.children[1];
256+
if (se.opcode == Opcode::I32Shl &&
257+
const_exp.etype == ExprType::Const) {
258+
auto& ce = *cast<ConstExpr>(const_exp.e);
259+
if (ce.const_.type == Type::I32 && (1 << ce.const_.u32) == align) {
260+
// Pfew, case detected :( Lets re-write this in Haskell.
261+
// TODO: we're decompiling these twice.
262+
// The thing to the left of << is going to be part of the index.
263+
auto ival = DecompileExpr(shift_exp.children[0], &shift_exp);
264+
if (ival.v.size() == 1) { // Don't bother if huge.
265+
if (offset == 0) {
266+
index = ival.v[0];
267+
} else {
268+
BracketIfNeeded(ival, Precedence::Add);
269+
index = cat(ival.v[0], " + ", index);
270+
}
271+
// We're going to use the thing to the left of + as the new
272+
// base address:
273+
val = DecompileExpr(addr_exp.children[0], &addr_exp);
274+
}
275+
}
276+
}
277+
}
278+
}
279+
BracketIfNeeded(val, Precedence::Indexing);
231280
val.v.back() +=
232-
!access.empty()
233-
? "." + access
234-
: cat("[", std::to_string(offset),
235-
"]:", GetDecompTypeName(GetMemoryType(op_type, opc)),
236-
!opc.IsNaturallyAligned(align)
237-
? cat("@" + std::to_string(align))
238-
: "");
281+
cat("[", index, "]:", GetDecompTypeName(GetMemoryType(op_type, opc)),
282+
opc.IsNaturallyAligned(align)
283+
? ""
284+
: cat("@", std::to_string(align)));
239285
}
240286

241287
Value DecompileExpr(const Node& n, const Node* parent) {

test/decompile/basic.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
if
2929
i32.const 1
3030
i32.const 2
31-
i32.load offset=3
31+
i32.load offset=3 align=1
3232
i32.const 5
3333
i32.add
3434
i32.store offset=4
@@ -126,7 +126,7 @@ export function f(a:int, b:int):int {
126126
var c:long = 8L;
127127
var d:float = 6.0f;
128128
var e:double = 7.0;
129-
if (e < 10.0) { 1[4]:int = 2[3]:int + 5 }
129+
if (e < 10.0) { 1[1]:int = 2[3]:int@1 + 5 }
130130
f(a + g_b, 9);
131131
loop L_b {
132132
if (if (0) { 1 } else { 2 }) goto B_c;

test/decompile/loadstore.txt

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,48 @@
5353
drop
5454
;; 4) Unaligned access / access with unexpected gaps.
5555
get_local 7
56-
f32.load offset=1
56+
f32.load offset=1 align=1
5757
drop
58+
;; Test index rewriting.
59+
;; code that does (base + (index << 2))[0]:int is super common.
60+
get_local 0
61+
get_local 1
62+
i32.const 2
63+
i32.shl
64+
i32.add
65+
get_local 0
66+
get_local 1
67+
i32.const 2
68+
i32.shl
69+
i32.add
70+
i32.load offset=0
71+
i32.store offset=0
72+
;; Same with non-zero offsets.
73+
get_local 0
74+
get_local 1
75+
i32.const 2
76+
i32.shl
77+
i32.add
78+
get_local 0
79+
get_local 1
80+
i32.const 2
81+
i32.shl
82+
i32.add
83+
i32.load offset=4
84+
i32.store offset=4
85+
;; If the shift amount does not match, it doesn't work.
86+
get_local 0
87+
get_local 1
88+
i32.const 3
89+
i32.shl
90+
i32.add
91+
get_local 0
92+
get_local 1
93+
i32.const 2
94+
i32.shl
95+
i32.add
96+
i32.load offset=4
97+
i32.store offset=4
5898
)
5999
(export "f" (func $f))
60100
)
@@ -77,7 +117,10 @@ export function f(a:{ a:float, b:float }, b:{ a:ushort, b:long }) {
77117
f[0]:short;
78118
g[0]:int;
79119
g[0]:int@1;
80-
h[1]:float;
120+
h[1]:float@1;
121+
a[b]:int = a[b]:int;
122+
a[b + 1]:int = a[b + 1]:int;
123+
(a + (b << 3))[1]:int = a[b + 1]:int;
81124
}
82125

83126
;;; STDOUT ;;)

0 commit comments

Comments
 (0)