❌

Normale weergave

Steinar H. Gunderson: Decompilation patterns, part 12: __builtin_memcpy

15 Oktober 2026 om 21:45

Say you see this lovely chunk of decompiled code:

    var_v1 = &sp10
    var_v0 = D_80010040;
    temp_s4 = var_v1;
    sp110 = &sp2C
    sp114 = &sp30
    do {
        var_v1->unk0 = (s32) var_v0->unk0;
        var_v1->unk4 = (s32) var_v0[1];
        var_v1->unk8 = (s32) var_v0[2];
        var_v1->unkC = (s32) var_v0[3];
        var_v0 += 0x10;
        var_v1 += 0x10;
    } while (var_v0 != &D_8001040[0x3C]);
    var_v1->unk0 = (s32) var_v0->unk0;
    var_v1->unk4 = (s32) var_v0[1];
    var_v1->unk8 = (s32) var_v0[2];

If you ignore the fact that m2c wrote ->unk0 for [0] (it has a bit of an annoying habit to do that), this is obviously something copying 16 bytes at a time and then 12 more bytes at the end. But would a programmer ever do this? Nope, this is an inlined, unrolled memcpy.

This is known in GCC as __builtin_memcpy, and it can happen either implicitly, or by some sort of lowering from a struct or array copy (e.g., through initialization of a stack array). Depending a bit on whether the variable is known to be aligned or not, you'll either get just lw or some evil lwr/lwl sequence; I'm always struggling a bit to change one to the other. In any case, what you want here is:

    __builtin_memcpy(&sp10, D_80010040, 0x4C);

and then make sp10 the right type instead of a scalar, ideally so that you can use sizeof() on it. Or, in some cases, a direct initialization:

    s32 foo[] = { 1, 2, 3, ...more data from D_80010040... };
  •  
❌