私は組み込みプロジェクトに取り組んでおり、マクロを使用して USART のレジスタへのアクセスを最適化するコードの一部にさらに構造を追加しようとしています。プリプロセッサの #define されたレジスタ アドレスを const 構造に整理したいと思います。構造体をマクロで複合リテラルとして定義し、それらをインライン化された関数に渡すと、gcc は十分に賢く、生成されたアセンブリでポインターをバイパスし、構造体メンバーの値をコードで直接ハードコーディングします。例えば:
C1:
struct uart {
volatile uint8_t * ucsra, * ucsrb, *ucsrc, * udr;
volitile uint16_t * ubrr;
};
#define M_UARTX(X) \
( (struct uart) { \
.ucsra = &UCSR##X##A, \
.ucsrb = &UCSR##X##B, \
.ucsrc = &UCSR##X##C, \
.ubrr = &UBRR##X, \
.udr = &UDR##X, \
} )
void inlined_func(const struct uart * p, other_args...) {
...
(*p->ucsra) = 0;
(*p->ucsrb) = 0;
(*p->ucsrc) = 0;
}
...
int main(){
...
inlined_func(&M_UART(0), other_parms...);
...
}
ここで、UCSR0A、UCSR0B、&c は、uart レジスタとして左辺値として定義されています。
#define UCSR0A (*(uint8_t*)0xFFFF)
gcc は構造リテラルを完全に排除することができ、inlined_func() に示されているようなすべての代入は、レジスタのアドレスをマシン レジスタに読み込む必要がなく、間接アドレス指定もなしに、レジスタ アドレスに直接書き込みます。
A1:
movb $0, UCSR0A
movb $0, UCSR0B
movb $0, UCSR0C
これは値を USART レジスタに直接書き込み、アドレスをマシン レジスタにロードする必要がないため、構造体リテラルをオブジェクト ファイルに生成する必要はまったくありません。構造体リテラルはコンパイル時の構造体になり、抽象化のために生成されたコードにコストはかかりません。
マクロの使用をやめたかったので、ヘッダーで定義された静的定数構造体を使用してみました。
C2:
#define M_UART0 M_UARTX(0)
#define M_UART1 M_UARTX(1)
static const struct uart * const uart[2] = { &M_UART0, &M_UART1 };
....
int main(){
...
inlined_func(uart[0], other_parms...);
...
}
ただし、gcc はここで構造体を完全に削除することはできません。
A2:
movl __compound_literal.0, %eax
movb $0, (%eax)
movl __compound_literal.0+4, %eax
movb $0, (%eax)
movl __compound_literal.0+8, %eax
movb $0, (%eax)
これにより、レジスタ アドレスがマシン レジスタにロードされ、間接アドレッシングを使用してレジスタに書き込まれます。とにかく、gccにC2 CコードのA1アセンブリコードを生成するよう説得できることを知っている人はいますか? __restrict 修飾子のさまざまな使用法を試しましたが、役に立ちませんでした。