gpt4 book ai didi

c - 防止 GCC 优化掉对内存映射地址的循环写入

转载 作者:行者123 更新时间:2023-12-03 17:12:23 26 4
gpt4 key购买 nike

我有一个指向像这样的控制端口的地址(对于上下文我正在开发 Sega/Megadrive 游戏):

volatile u32 * vdp_ctrl = (u32 *) 0x00C00004;

以及我想设置的一组初始值:
const u8 initial_vdp_vals[24] = {
0x20,
0x74,
0x30,
..etc
};

有一个循环:
typedef struct {
u16 upper;
u8 reg;
u8 val;
} bitset;

typedef union {
bitset b;
u32 as_u32;
} u_bitset;

static inline void init_vdp() {
u_bitset cmd = {{0x00008000}};
for(int i = 0; i < 24; i++) {
cmd.b.val = initial_vdp_vals[i];
*vdp_ctrl = cmd.as_u32;
cmd.b.reg += 1;
}
}

问题是 gcc(至少使用 O2)优化了它并且只将最后一个值写入 *vdp_ctrl指针。我设法通过设置 bitset 中的一个属性来解决这个问题。结构到 volatile ,但这似乎不是一个直观的解决方案,因此它生成的程序集非常冗长。

所以我的问题有两个:
  • 向 gcc 建议我的 vdp_ctrl 的“正确”方式是什么?随着时间的推移,指针需要接受多次写入。我将经常使用这种模式(将常量/静态数据写入循环中的控制/数据地址),并将随机字段标记为 volatile 似乎不直观。
  • 我用于生成的 asm 的“质量标准”如下所示(不是 gcc 生成的):
  •     move.l #initial_vdp_vals, a0
    move.l #24, d0
    move.l #0x00008000, d1

    .copy:
    move.b (a0)+, d1
    move.w d1, 0x00C00004
    add.w #0x0100, d1
    dbra d0, .copy

    这是非常好的和简洁的。所以我的另一个问题可能是(作为一个完整的 C 新手):我的 C 解决方案是否有更好的方法让我更接近上面的程序集?老实说,我什至不确定我的代码是否正确,因为我只是想先解决这个循环优化问题,因为我知道这将是一个持续不断的问题。

    产生我的问题的可运行示例:
    volatile unsigned long * vdp_ctrl = (unsigned long *) 0x00C00004;

    const unsigned char initial_vdp_vals[24] = {
    0x20,
    0x74,
    0x30,
    0x40,
    0x05,
    0x70,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x08,
    0x81,
    0x34,
    0x00,
    0x00,
    0x01,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00
    };

    typedef struct {
    unsigned int upper;
    unsigned char reg;
    unsigned char val;
    } bitset;

    typedef union {
    bitset b;
    unsigned long as_u32;
    } u_bitset;

    static inline void init_vdp() {
    u_bitset cmd = {{0x00008000}};
    for(int i = 0; i < 24; i++) {
    cmd.b.val = initial_vdp_vals[i];
    *vdp_ctrl = cmd.as_u32;
    }
    }

    void init() {
    init_vdp();
    for(;;) {
    }
    }

    m68k-linux-gnu-gcc -ffreestanding -O2 -S -c test.c -o test.s .生成以下内容:
    #NO_APP
    .file "test.c"
    .text
    .align 2
    .globl init
    .type init, @function
    init:
    link.w %fp,#0
    move.l vdp_ctrl,%a0
    moveq #24,%d0
    move.l #32768,%d1
    .L2:
    move.l %d1,(%a0)
    subq.l #1,%d0
    jne .L2
    .L3:
    jra .L3
    .size init, .-init
    .globl initial_vdp_vals
    .section .rodata
    .type initial_vdp_vals, @object
    .size initial_vdp_vals, 24
    initial_vdp_vals:
    .byte 32
    .byte 116
    .byte 48
    .byte 64
    .byte 5
    .byte 112
    .byte 0
    .byte 0
    .byte 0
    .byte 0
    .byte 0
    .byte 8
    .byte -127
    .byte 52
    .byte 0
    .byte 0
    .byte 1
    .byte 0
    .byte 0
    .byte 0
    .byte 0
    .byte 0
    .byte 0
    .byte 0
    .globl vdp_ctrl
    .data
    .align 2
    .type vdp_ctrl, @object
    .size vdp_ctrl, 4
    vdp_ctrl:
    .long 12582916
    .ident "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
    .section .note.GNU-stack,"",@progbits
    gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)

    备注 :看来我的数组的大小决定了它是否得到优化。当我只制作两个元素时,它没有优化。

    最佳答案

    在这种代码中,您应该使用精确大小的整数。我强烈建议打包结构和 union 。

    #include <stdint.h>
    #define vdp_ctrl ((volatile uint32_t *) 0x00C00004)

    const unsigned char initial_vdp_vals[24] = {
    0x20,
    0x74,
    0x30,
    0x40,
    0x05,
    0x70,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x08,
    0x81,
    0x34,
    0x00,
    0x00,
    0x01,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00
    };

    typedef struct {
    uint16_t upper;
    uint8_t reg;
    uint8_t val;
    } __attribute__((packed)) bitset;

    typedef union {
    bitset b;
    uint32_t as_u32;
    } __attribute__((packed)) u_bitset ;

    static inline void init_vdp() {
    u_bitset cmd = {.b.upper = 0x00008000};
    for(int i = 0; i < 24; i++)
    {
    cmd.b.val = initial_vdp_vals[i];
    *vdp_ctrl = cmd.as_u32;
    }
    }

    void init() {
    init_vdp();
    for(;;) {
    }
    }

    它会生成您需要的代码。

    IMO 最好有宏而不是真实对象。在这个琐碎的代码中它可能没有任何区别,但如果代码变得更复杂,它会产生任何区别。

    关于c - 防止 GCC 优化掉对内存映射地址的循环写入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60459889/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com