@@ -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) {
0 commit comments