GCC Generated a Misaligned "ldrd" Instruction

Viewed 785

When compiling this C++ code:

#include "../SM64DS_2.h"

//@021026a0, hooks: 0x020bddb8, 0x020bde0c
void CancelPowerups(Player& player)
{
    if(player.isFreezeFramePeach)
    {
        player.isFreezeFramePeach = false;
        player.EndMusic(0x33);
    }
    if(player.isDashyToad)
    {
        player.isDashyToad = false;
        player.EndMusic(0x33);
    }
}

//hook: 0x020b98f8
void EnableFreezeFramePeach(Player& player)
{
    //TODO: Show message on first use
    player.isFreezeFramePeach = true;
    player.flowerPowerTimer = 600;
    player.SetMusic(0x33);
}

//hook: 0x020b9904
void EnableDashyToad(Player& player)
{
    //TODO: Show message on first use
    player.isDashyToad = true;
    player.flowerPowerTimer = 600;
    player.SetMusic(0x33);
}

//hook: 0x020e4f10
void WorkDashyToadPart1(Player& player)
{
    //TODO: Deal with speed of walking animation
    if(player.isDashyToad)
    {
        player.horzSpeed /= 2;
        player.isSpeedHalfed = true;
        if(player.walkOnWaterTimer == 61)
            ++player.walkOnWaterTimer;
    }
}

//hook: 0x020e52e8
void WorkDashyToadPart2(Player& player)
{
    if(player.isSpeedHalfed)
    {
        player.horzSpeed *= 2;
        player.isSpeedHalfed = false;
        player.dashyToadParticleSysID = Particle::System::New(player.dashyToadParticleSysID,
                                                              0x4a,
                                                              player.pos.x,
                                                              player.pos.y + Fix12i(72),
                                                              player.pos.z,
                                                              nullptr,
                                                              nullptr);
    }
}

with the following flags for gcc

arm-none-eabi-g++ -MMD -MP -MF /c/Users/Joshua/Documents/desmume-0.9.11-win64/SM64DSe/SM64DSe/ASMPatchTemplate/build/PeachToadAbilities.d -g -Wall -O2 -march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math  -iquote /c/Users/Joshua/Documents/desmume-0.9.11-win64/SM64DSe/SM64DSe/ASMPatchTemplate/include -I/c/devkitPro/libnds/include -I/c/devkitPro/devkitARM/include -I/c/devkitPro/devkitARM/arm-none-eabi/include -I/c/Users/Joshua/Documents/desmume-0.9.11-win64/SM64DSe/SM64DSe/ASMPatchTemplate/build -DARM9 -nodefaultlibs -I. -fno-builtin -c --std=c++14 -fno-rtti -fno-exceptions -Wa,-adl=PeachToadAbilities.lst -c /c/Users/Joshua/Documents/desmume-0.9.11-win64/SM64DSe/SM64DSe/ASMPatchTemplate/source/C_PeachToadAbilities/PeachToadAbilities.cpp -o PeachToadAbilities.o

(Note particularly that the architecture is ARMv5TE and the CPU is an ARM946E-S) The following ASM is generated for the last function:

push    {r4, lr}
mov     r4, r0
mov     r1, #0x0
ldrd    r2, [r4, #0x5c]
ldr     ip, [r0, #0x98]
ldr     r0, [r0, #0x280]
lsl     ip, ip, #0x1
str     ip, [r4, #0x98]
ldr     ip, [r4, #0x64]
sub     sp, sp, #0x10
strb    r1, [r4, #0x27d]
add     r3, r3, #0x48000
str     r1, [sp, #0x8]
str     r1, [sp, #0x4]
str     ip, [sp]
mov     r1, #0x4a
bl      _ZN8Particle6System3NewEjj5Fix12IiES2_S2_PK11Vector3_16fPNS_8CallbackE
str     r0, [r4, #0x280]
add     sp, sp, #0x10
pop     {r4, pc}

For context, this is a piece of a ROM hack of a Nintendo DS game. The Player structure is 4-byte aligned, and at offset 0x5C is the player's position, which is a structure of 3 Fix12<int> (A Fix12<int> is a structure of 1 4-byte integer.).

The problem is that gcc generates an "ldrd" instruction that cannot be proven to access an 8-byte aligned address (It shouldn't know where "player" is and besides, 0x5C is not a multiple of 8.). The ARM documentations that I've looked at say that for ARMv5, loading a misaligned doubleword is undefined behavior. However, no$gba and DeSmuME assume that such an access when the address is 4-byte aligned still loads an 8-byte value from that address. Is this correct behavior for ARMv5TE on an ARM946E-S CPU? (I haven't tested this on an actual DS because I don't have a flash card.)

2 Answers

I have also seen this with ARMv5TE and ARM946E-S and GCC 6 ( was OK in 5). In my case, the code was loading two consecutive elements of an array, but there is no way for the compiler to prove that the array pointer passed in was 8-byte aligned. I will try to generate a simple example and report this. You should do the same in the hope it will be fixed.

Most probably the class instances are aligned to certain bytes (64 bytes for example), and the header isn't a multiple of eight bytes, hence the member variables are packed in a location starting at an "unaligned" address so that combined with an "unaligned" offset such as 0x5c, the address is guaranteedly 8 bytes aligned.

Compilers aren't perfect, but I cannot imagine them doing such trivial mistakes.

So don't worry.

Related