Aub*_*pez 3 c gcc arm devkitpro game-boy-advance
我正在开发一个简单的 Gameboy Advance ROM,并试图理解为什么以下代码适用于 gcc选项,但使用或以上-O0时会崩溃(白色模拟器屏幕) :-O1
int main () {
// Set video mode 3 and background 2
*(unsigned int*)0x04000000 = 0x0403;
int x;
for(x = 0; x < 1; x++){
// Set a single pixel at position (120, 80) in VRAM to red
((unsigned short*)0x06000000)[120+80*240] = 0x001F;
};
while(1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该循环显然是不必要的,但我用它来创建一个崩溃行为的最小示例。如果没有循环,无论优化级别如何,代码都可以正常工作。通过循环,它会在-O1和更高处崩溃,但可以在-O0.
无论我是否实际x在循环体中使用(例如,用于x计算像素位置),都会发生相同的行为。据我所知,每当我尝试在循环中进行这种类型的直接内存修改时,我都会在更高的优化级别上遇到损坏。
这里发生了什么?什么优化破坏了代码?这是否表明我做事的方式存在问题?谢谢你的帮助!
更多细节:
arm-none-eabi-gcc -MMD -MP -MF /path/to/myfile.d -g -Wall -O1 -mcpu=arm7tdmi -mtune=arm7tdmi -mthumb -mthumb-interwork -iquote /path/to/include -I/opt/devkitpro/libgba/include -I/path/to/build -c /path/to/source/myfile.c -o myfile.o
Run Code Online (Sandbox Code Playgroud)
对内存映射硬件的写入通常需要通过volatile类型指针来完成,例如*(volatile unsigned int*)0x04000000 = 0x0403;. 否则,编译器可以自由地假设它们没有副作用,并且可以以意想不到的方式进行优化。在这里,这既适用于设置视频模式的存储,也适用于放置像素的存储。
(实际上,当写入视频内存时,您可能可以允许进行一些优化;例如,您不关心所有写入是否完全按照指定的顺序发生,只要它们最终都发生即可。在这种情况下,您通常可以得到远离非volatile存储以及某种特定于编译器的内存屏障,其行为就好像它可以观察它们一样,例如asm("" : : : "memory");在 gcc 中)。
在这种情况下发生的情况是编译器优化了第一个存储,它设置了视频模式: https: //godbolt.org/z/4dn9dhh6j
main:
ldr r3, .L3
mov r2, #31
strh r2, [r3, #240] @ movhi
.L2:
b .L2
.L3:
.word 100701696
Run Code Online (Sandbox Code Playgroud)
我不是 gcc 优化器细节方面的专家,但我的假设是编译器认为在存储和无限循环之间的任何地方都没有从内存中读取数据。因此,它假设程序中的任何代码都无法读回写入到 address 的值0x04000000,因为无限循环后面的任何内容(包括 之外的代码main)都无法执行。如果该值永远无法被读取,并且写入本身没有副作用(假设因为它不是volatile),那么它就是一个死存储,并且首先不需要写入它。
然而,按照这种逻辑,人们会认为它也会优化放置像素的存储,但事实并非如此。所以也许我还缺少其他事情。