Linuxカーネルドライバー(ARM用)を作成していて、irqハンドラーで割り込みビットをチェックする必要があります。
bit
0/16 End point 0 In/Out interrupt
(very likely, while In is more likely)
1/17 End point 1 In/Out interrupt
...
15/31 End point 15 In/Out interrupt
一度に1ビット以上を設定できることに注意してください。
だからこれはコードです:
int i;
u32 intr = read_interrupt_register();
/* ep0 IN */
if(likely(intr & (1 << 0))){
handle_ep0_in();
}
/* ep0 OUT */
if(likely(intr & (1 << 16))){
handle_ep0_out();
}
for(i=1;i<16;++i){
if(unlikely(intr & (1 << i))){
handle_ep_in(i);
}
if(unlikely(intr & (1 << (i + 16)))){
handle_ep_out(i);
}
}
(1 << 0)
ただし、(1 << 16)
コンパイル時に計算されますが、計算されません。また、ループには積分の比較と加算があります。(1 << i)
(1 << (i + 16))
これはirqハンドラーであるため、作業は最短時間で実行する必要があります。これにより、少し最適化する必要があるかどうかを考えさせられます。
可能な方法は?
1.ループを分割しますが、違いはないようです...
/* ep0 IN */
if(likely(intr & (1 << 0))){
handle_ep0_in();
}
/* ep0 OUT */
if(likely(intr & (1 << 16))){
handle_ep0_out();
}
for(i=1;i<16;++i){
if(unlikely(intr & (1 << i))){
handle_ep_in(i);
}
}
for(i=17;i<32;++i){
if(unlikely(intr & (1 << i))){
handle_ep_out(i - 16);
}
}
2.intr
比較する値の代わりにシフトしますか?
/* ep0 IN */
if(likely(intr & (1 << 0))){
handle_ep0_in();
}
/* ep0 OUT */
if(likely(intr & (1 << 16))){
handle_ep0_out();
}
for(i=1;i<16;++i){
intr >>= 1;
if(unlikely(intr & 1)){
handle_ep_in(i);
}
}
intr >>= 1;
for(i=1;i<16;++i){
intr >>= 1;
if(unlikely(intr & 1)){
handle_ep_out(i);
}
}
3.ループを完全に展開します(図には示されていません)。それはコードを少し厄介にするでしょう。
4.他にもっと良い方法はありますか?
5.それとも、コンパイラが実際に最も最適化された方法を生成するということですか?
編集:私はgccコンパイラにその特定のループを展開するように指示する方法を探していましたが、私の検索によればそれは不可能のようです...