8ビットMCUでのC整数プロモーション


14

例としてavr-gccを使用すると、int型は16ビット幅に指定されます。Cの8ビットオペランドで演算を実行すると、Cでの整数の昇格により、それらのオペランドが16ビットint型に変換されます。これは、AVRでの8ビット算術演算はすべて、C Cの整数昇格のためにアセンブリで記述されている場合


1
私はそうは思わない、コンパイラは宛先変数が(符号なし)文字であることを認識するので、上位8ビットを計算することに煩わされないだろう。それでも、GCCはコードを最適化するのにあまりよくないことがあるため、ASMでコーディングすると、結果のMGIHTが高速になることがわかりました。ただし、非常にタイムクリティカルなタスク/割り込みを非常に強力な予算制約で実行している場合を除き、より強力なプロセッサを選択してCでプログラムするか、パフォーマンスの低下を心配しないでください(代わりに時間を考慮してください) -市場投入、コードの読みやすさ/再利用性の向上、バグの減少など。
ネクストハック

チェックする時間がないことをおizeびします。ただし、gccには「整数プロモーション」を制御するコマンドラインフラグがあったと思います。特定のコードに対してそれを制御するプラグマもあるかもしれません。パフォーマンスはどれほど重要ですか?AVRの多くの用途では、一部の演算の速度の違いは問題になりません。Foxusが最初にコードを正しく動作させることについて。次に、パフォーマンスの問題がある場合は、それが何であるかを調べます。アセンブラでのコーディングに時間を浪費することは簡単ですが、それが問題ではないとわかるだけです。
gbulmer

1
単に逆アセンブルして、コンパイラが何をしているかを見てください。純粋な言語の観点からはい。ここでの実装は非定型です。通常、intはレジスタサイズに合わせて調整しようとします。16ビットのレジスタがある場合、8ビットの数学は8より16ビットで実際に安価です。しかし、これは逆で、8ビットmcuでは16ビットとして。したがって、おそらくこれを気にするところではucharを使用するべきですが、他のどこでもあなたを傷つけるので、それを一般的なプログラミング習慣にしないでください。
old_timer

3
要確認:コメントで質問に答えないでください。
パイプ

4
この種の質問は、SOのCの専門家に尋ねる方が良いです。これは純粋なソフトウェアの質問だからです。Cでの整数プロモーションはやや複雑なトピックです-平均的なCプログラマーはそれについて多くの誤解を抱くでしょう。
ランディン

回答:


16

短い話:

16ビットへの整数の昇格は常に行われます-C標準はこれを実施します。しかし、コンパイラーは、型が昇格された場合と同じ符号になると推測できる場合、計算を8ビットまで最適化して戻すことができます(組み込みシステムコンパイラーは通常、このような最適化にかなり適しています)。

これは常にそうではありません!整数の昇格によって引き起こされる暗黙的な署名の変更は、組み込みシステムのバグの一般的な原因です。

詳細な説明は、暗黙的なタイププロモーションルールにあります


8
unsigned int fun1 ( unsigned int a, unsigned int b )
{
    return(a+b);
}

unsigned char fun2 ( unsigned int a, unsigned int b )
{
    return(a+b);
}

unsigned int fun3 ( unsigned char a, unsigned char b )
{
    return(a+b);
}

unsigned char fun4 ( unsigned char a, unsigned char b )
{
    return(a+b);
}

予想どおりfun1はすべてintなので、16ビットの数学も同様です

00000000 <fun1>:
   0:   86 0f           add r24, r22
   2:   97 1f           adc r25, r23
   4:   08 95           ret

コードによって呼び出される16ビットの追加であるため、技術的には正しくありませんが、最適化されていない場合でも、結果のサイズのためにこのコンパイラはadcを削除しました。

00000006 <fun2>:
   6:   86 0f           add r24, r22
   8:   08 95           ret

ここでプロモーションが実際に驚くことはありませんでしたが、コンパイラはこれを実行していませんでした驚くことではなく、uchar mathを実行するように言った

0000000a <fun3>:
   a:   70 e0           ldi r23, 0x00   ; 0
   c:   26 2f           mov r18, r22
   e:   37 2f           mov r19, r23
  10:   28 0f           add r18, r24
  12:   31 1d           adc r19, r1
  14:   82 2f           mov r24, r18
  16:   93 2f           mov r25, r19
  18:   08 95           ret

そして理想は、8ビットであることを知っており、8ビットの結果が欲しいので、8ビットを最後までやり遂げるように言っただけです。

0000001a <fun4>:
  1a:   86 0f           add r24, r22
  1c:   08 95           ret

そのため、一般的に、コンパイラーの作成者が妥協しなければならなかったこのような8ビットmcuの場合、理想的には(u)intのサイズであるレジスターのサイズを目指した方が良いです... 8ビット以上を必要としない数学にucharを使用する場合、コードを移動したり、より大きなレジスタを備えたプロセッサでそのような新しいコードを記述したりすると、コンパイラーはマスキングと符号拡張を開始する必要があります。その他はいけない。

00000000 <fun1>:
   0:   e0800001    add r0, r0, r1
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e0800001    add r0, r0, r1
   c:   e20000ff    and r0, r0, #255    ; 0xff
  10:   e12fff1e    bx  lr

8ビットのコストを強制します。私は少し/多くをcheしましたが、これをもっと公平に見るには少し複雑な例を必要とします。

コメントディスカッションに基づく編集

unsigned int fun ( unsigned char a, unsigned char b )
{
    unsigned int c;
    c = (a<<8)|b;
    return(c);
}

00000000 <fun>:
   0:   70 e0           ldi r23, 0x00   ; 0
   2:   26 2f           mov r18, r22
   4:   37 2f           mov r19, r23
   6:   38 2b           or  r19, r24
   8:   82 2f           mov r24, r18
   a:   93 2f           mov r25, r19
   c:   08 95           ret

00000000 <fun>:
   0:   e1810400    orr r0, r1, r0, lsl #8
   4:   e12fff1e    bx  lr

驚きません。なぜオプティマイザーは余分な命令を残したのに、r19でldiを使用できないのですか?(私はそれを尋ねたときに答えを知っていた)。

EDIT2

AVR用

avr-gcc --version
avr-gcc (GCC) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

悪い習慣を避けるため、または8ビットの比較ではありません

arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 7.2.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

明らかに、最適化がオンになっているのは、自分のコンパイラと比較して、それが私の出力とどのように比較されるかを確認するのに数秒しかかかりませんが、とにかく:

whatever-gcc -O2 -c so.c -o so.o
whatever-objdump -D so.o

はい、確かにavr、picなどで、バイトサイズの変数にバイトを使用すると、メモリを節約できます。実際に使用する場合は、実際にそれを使用する必要があります。できるだけ多くのレジスタにメモリ内に配置するため、追加の変数がないことでフラッシュの節約が実現します。RAMの節約は実際の場合とそうでない場合があります。


2
「コンパイラは以前はこれを行っていませんでしたが、どのバージョンがこの開始を引き起こしたのかわからず、私のキャリアの早い段階でこれに遭遇しました。コンパイラは順不同で(上記のように)プロモーションを行いましたが、uchar math驚きません。」組み込みシステムのCコンパイラでは、コンパイラは通常、最適化することが許可されている:)恐ろしい標準適合性を持つために使用されているので、しかし、ここでは結果がで収まることを控除することはできませんunsigned char、それはので、持っている 16ビットへのプロモーションを行うために必要に応じて、標準で。
ランディン

1
@old_timer (a<<8)|bは、int16ビットのシステムでは常に間違っています。aint署名されている暗黙的に昇格されます。場合にはaMSBの値を保持は、未定義の動作を呼び出す16ビット数の符号ビットにそのデータをシフトしてしまいます。
ランディン

1
fun3はfun..ny ...コンパイラによってまったく最適化されていません... GCCではr1は常に0であり、変数a、b、および結果のレジスタをra、rb、{rh、rl}であると見なし、コンパイラは次を実行できます。1)mov rh、r1; 2)mov rl、ra; 2)rl、rbを追加します。3)adc rh、rh; 4)ret。4命令、vs 7または8 ...命令1はldi rh、0で変更できます。
ネクストハック

1
コンパイラと使用中の関連オプションが指定されている場合、これはより良い答えになります。
ラッセルボロゴーブ

1
int / charなどの使用を避け、代わりにはるかに明示的で読みやすいint16_tおよびint8_tを使用することをお勧めします。
ユーザー

7

必ずしもではありません。現代のコンパイラーは、生成されたコードの最適化に優れた仕事をしているからです。例えばz = x + y;、すべての変数がunsigned charである場所を記述する場合unsigned int、計算を実行する前にそれらをプロモートする必要があります。ただし、最終結果は昇格なしではまったく同じになるため、コンパイラは8ビット変数を追加するだけのコードを生成します。

もちろん、これは常に当てはまるわけではありません。たとえば、の結果はz = (x + y)/2;上位バイトに依存するため、昇格が行われます。中間結果をにキャストすることにより、アセンブリに頼らずに回避できunsigned charます。

このような非効率性の一部は、コンパイラオプションを使用して回避できます。たとえば、多くの8ビットコンパイラには、intCで必要な代わりに列挙型を1バイトに収めるためのプラグマまたはコマンドラインスイッチがあります。


4
「コンパイラは、それらをunsigned intに昇格させる必要があります」。いいえ、コンパイラはそれらを促進するために必要とされるintことから、char最も可能性が高いと同じ変換ランクを持っていないであろうint任意のプラットフォーム上。
ランディン

3
「たとえば、多くの8ビットコンパイラには、Cが必要とするintではなく、列挙型を1バイトに収めるためのプラグマまたはコマンドラインスイッチがあります。」C標準では、列挙変数を1バイトで割り当てることができます。列挙定数が必要なだけですint(はい、一貫性がありません)。C11 6.7.2.2Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined...
ランディン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.