アライメントの話

stellarisのようなARMのCPUでは、メモリアクセスの際のアドレス制約があるので、コンパイラは構造体をアライメントに従って配置してくれます。しかし、ローンチパッドのようにRAMが少なかったり、構造体をバッファからシリアライズ・デシリアライズしたい場合なんかは、アライメントが邪魔になるので、pragmaで目的の構造体のアライメントを調整したりします。stellarisの環境ではどうするのか調べました。

検索してみると、まずこのページが見つかります。

http://e2e.ti.com/support/microcontrollers/stellaris_arm/f/471/p/56616/201142.aspx

2010年の話ですが、gcc拡張の __attribute__ *1 は使えるけれども、グローバルにアライメント設定する機能は無いと頑なに言っています。CCSのコンパイラのプロパティをみると、ARM Compiler->Advanced Options->Runtime Model Optionsに --align_structs オプションが存在しますので、このオプションを1にしてコンパイルリンクしてみます。

#include <stdint.h>

struct stTest {
    uint8_t a;
    uint16_t b;
    uint8_t c;
    uint32_t d;
};
struct stTest stt;

void main(void) {
    int i = sizeof(stt);

    stt.a = 1;
    stt.b = 2;
    stt.c = 3;
    stt.d = 4;
}
0x2000114 stt
0x2000114 00020001 00000003 00000004

結果、stTestのサイズは12で、1バイトアライメントは効きませんでした。続いて、stTestに __attribute__ *2 を指定してみると、結果はstTestのサイズが8バイトになりました。こちらは効いてます。

0x2000114 stt
0x2000114 03000201 00000004

次に、__attribute__ *3 の書き方をいくつか試してみました。

#include <stdint.h>

struct stA1 {
    uint8_t a;
    uint16_t b;
    uint8_t c;
    uint32_t d;
};

struct stA2 {
    uint8_t a;
    uint16_t b;
    uint8_t c;
    uint32_t d;
} __attribute__ ((packed));

struct stB1 {
    uint8_t e;
    uint8_t f[5];
    uint32_t g;
    struct stA1 h;
} __attribute__ ((packed));

struct stB2 {
    uint8_t e;
    uint8_t f[5];
    uint32_t g;
    struct stA1 h;
};

typedef struct {
    uint8_t e     __attribute__ ((packed));
    uint8_t f[5]  __attribute__ ((packed));
    uint32_t g    __attribute__ ((packed));
    struct stA1 h __attribute__ ((packed));
} stB3_t;

struct stB4 {
    uint8_t e;
    uint8_t f[5];
    uint32_t g;
    struct stA2 h;
} __attribute__ ((packed));

struct stB1 stb1;
struct __attribute__ ((packed)) stB2 stb21;
struct stB2 stb22;
stB3_t stb3;
struct stB4 stb4;

void main(void) {
    int i;
    i = sizeof(stb1);
    i = sizeof(stb21);
    i = sizeof(stb22);
    i = sizeof(stb3);
    i = sizeof(stb4);
}

サイズは、stb1から順に、22、24、24、22、18となりました。__attribute__*4 は構造体の定義で記述しないと効かなかったり、構造体の構造体とかは、両方の構造体で__attribute__ *5しておかないと効かなかったりしますね。packedを使うのは、サイズがシビアな環境だと思うので、きちんと効果がでるように書きたいです。しかしながら、アライメントをまたぐと、メモリアクセス回数が単純に増えたり、キャッシュの読み直しが起きたりでパフォーマンスには不利らしいです。

ちなみに、stellarisのコンパイラTMS470のVer.5.1のマニュアルには#pragma PACK(n)についての記載もありました。pragma が使えると、ちまちま__attribute__ *6を書かなくてすみますね。

http://www.ti.com/lit/ug/spnu151i/spnu151i.pdf

*1:packed

*2:packed

*3:packed

*4:packed

*5:packed

*6:packed