でgcc/g++を伝えてくれるので無視し私は?
-
29-09-2019 - |
質問
ンパイルするときにC/C++コードを使用gcc/g++の場合は無視さっ登録できるので教えてくれますか?例えば、このコード
int main()
{
register int j;
int k;
for(k = 0; k < 1000; k++)
for(j = 0; j < 32000; j++)
;
return 0;
}
jとして登録すが、このコード
int main()
{
register int j;
int k;
for(k = 0; k < 1000; k++)
for(j = 0; j < 32000; j++)
;
int * a = &j;
return 0;
}
jる通常の可変となります。できるので教えてるかどうかの変数を使って登録が保存されCPUす。
解決
GCCが無視しているとかなり仮定できます register
おそらく除くキーワード -O0
. 。ただし、何らかの形で違いを生むべきではありません。そのような深みがある場合は、すでにアセンブリコードを読んでいるはずです。
このトピックに関する有益なスレッドは次のとおりです。 http://gcc.gnu.org/ml/gcc/2010-05/msg00098.html 。昔に戻って、 register
実際、コンパイラが変数をレジスタに割り当てるのを助けましたが、今日の登録割り当ては、ヒントなしで自動的に最適に達成できます。キーワードは、cで2つの目的を果たし続けています。
- Cでは、変数のアドレスを取得することを防ぎます。レジスタにはアドレスがないため、この制限は簡単なCコンパイラに役立ちます。 (シンプルなC ++コンパイラは存在しません。)
- a
register
オブジェクトは宣言できませんrestrict
. 。なぜならrestrict
アドレスの関係は、それらの交差点は無意味です。 (C ++にはまだありませんrestrict
, 、そしてとにかく、このルールは少し些細なことです。)
C ++の場合、キーワードはC ++ 11および 除去が提案されています 2017年に予定されている標準改訂から。
一部のコンパイラが使用しています register
パラメーター宣言では、関数の呼び出し条約を決定し、ABIにより混合スタックとレジスタベースのパラメーターを許可します。これは不適合であるように見えますが、次のような拡張構文で発生する傾向があります register("A1")
, 、そして、そのようなコンパイラがまだ使用されているかどうかはわかりません。
他のヒント
現代の編集と最適化のテクニックに関して、 register
注釈はまったく意味がありません。 2番目のプログラムでは、住所を取得します j
, 、およびレジスタにはアドレスがありませんが、1つの同じローカルまたは静的変数は、その生涯に、または時にはメモリ、時にはレジスタに完全に保存されます。確かに、最適化コンパイラは、効果がないため、ネストされたループを何もまとめてコンパイルし、最終的な値を単純に割り当てます。 k
と j
. 。残りのコードがこれらの値を使用しないため、これらの割り当てを省略します。
Cのレジスタのアドレスを取得することはできません。さらに、コンパイラはあなたを完全に無視できます。 C99標準、セクション6.7.1 (PDF):
実装は、登録宣言を単に自動宣言として扱う場合があります。ただし、アドレス指定可能なストレージが実際に使用されているかどうかにかかわらず、ストレージクラスの仕様レジスタで宣言されたオブジェクトのどの部分のアドレスを計算することはできません(6.5.3.2で説明されている単位と演算子の使用)または暗黙的に( 6.3.2.1で説明したように、配列名をポインターに変換することにより)。したがって、ストレージクラスの仕様レジスタで宣言された配列に適用できる唯一の演算子は、sizeofです。
8ビットのAVRや写真を扱っていない限り、コンパイラはおそらくあなたがあなたが最もよく知っていると思ってあなたの嘆願を無視するとあなたが笑うでしょう。彼らの上でさえ、私は数回よく知っていて、コンパイラーをだまして(インラインASMを使用して)トリックする方法を見つけましたが、私のコードは爆発しました。
この質問、回答、その他いくつかの議論には登録キーワードなどの見--ようと暗黙のうちにすべて地元の人々がマップされたいずれかの特定の登録、または特定のメモリ位置のスタックです。これは一般的にtrueになるまで15-25年前、このときにtrueをoffに最適なインターネット上で 際標準の最適化を行います。地元の人々が現れる恐として象徴的な名前をご使用になることを記述するデータの流れとしてではなく、値が保存されました。
注意:は、"地元がここで:スカラー変数のスストレージクラス自動車(または登録)などのオペランドの'&'.コンパイラでコンパイル時に破オート構造体、労組や配列の個々の地区)に変数です。
これを説明するために:と書いているこの機能:
int factor = 8;
..のみのご利用 factor
変数を掛ける様々なもの:
arr[i + factor*j] = arr[i - factor*k];
この場合にくい-はありません factor
可変となります。このコード解析することを示 factor
は必ず8ので、すべてのシフトをして <<3
.かった場合には、代わりに同じものは1985年にはC、 factor
い場所のスタック、あるいmultipilies、コンパイラでコンパイル基本的にはた声明ではなかった覚えていないの価値を変数です。そのプログラマーがが #define factor 8
くらコードをこの状況を維持しつつ、調整可能 factor
.
ご利用の場合 -O0
(最適化)-使実際の取得変数 factor
.ることができますし、例えば、ステップの factor=8
決変更してく factor
11デバッガので、辛抱しましょう。そのためには、コンパイラについていけない 何 にレジスタ間の諸表以外の変数を割り当て特定のレジスタ;その場合、デバッガお知らせです。とすることはできない'になっていないの価値観の変数は、デバッガを変更します。つまり、必要なものは、1985年の状況を変更したい場合はローカル変数をデバッグしやすくなります。
現代のコンパイラはコンパイル機能としており
(1)ローカル変数が割り当てられたときの機能をコンパイラ生成の異なる'バージョンの変数でそれぞれが割り当てます。すべての読み込みの変数を参照して特定バージョン。
(2)それぞれの地元の人々が割り当てられるバーチャル'登録できます。中間の計算結果も割り当ての変数/登録;なので
a = b*c + 2*k;
となりのようなもの
t1 = b*c;
t2 = 2;
t3 = k*t2;
a = t1 + t3;
(3)コンパイラしてこれらの操作、普通subexpressions。それぞれの新しいレジスタにのみに記された一であることにより入れ替えを維持しつつ正.んもループを解析する
(4)コンパイラのその後を地図はこれらすべての仮想レジスタへの実際の登録するためのコードを生成します。.それぞれの仮登録は寿命での再利用の実際の登録も、多くのt1の上限まで必要に応じ、追加を生成する'a'ることができたに登録してい'a'.があり、私たちは現在以下の登録、一部のバーチャルレジスタ割り当てることができるメモリは、値で開催される一定の登録-保存-メモリを搭載に戻れることによって登録します。に負荷-store機械が、その価値を登録で使用できる計算が、この第二戦略accomodatesる。
以上から、この明確であるべきである:であると判断した仮想の登録にマッピングされ factor
と同一の定数'8'、すべてのmultiplicationsによる factor
はmultiplications8.場合でも factor
変更された後、'new'の変動に支障を生じない限度において事前の利用 factor
.
も示唆さっき、
vara = varb;
..することもできる場合がございますので、対応するコピーのコードです。のためのインスタンス
int *resultp= ...
int acc = arr[0] + arr[1];
int acc0 = acc; // save this for later
int more = func(resultp,3)+ func(resultp,-3);
acc += more; // add some more stuff
if( ...){
resultp = getptr();
resultp[0] = acc0;
resultp[1] = acc;
}
上記の'バージョンのacc(初期に後を加えることができます。')が二つの異なるレジスタ、acc0"がそれと同じinital"acc'.なので登録コピーが必要なのは、"acc0=acc'.別のポイント:のresultp'が割り当てられ、およびそれ以降の課題を無視し前の値が基本的に二つの異なる'resultp'変数のコードで簡単に定める。
意味するものです:んにあまり積極的に複雑な表現をさらに小さなものを追加地元の人々のための中間体の場合、これはコードを容易にします。が基本的にゼロの実行時の罰則このオプティマイザは同じこと。
について興味のある方はもちろん学ぶりを開始することができる。 http://en.wikipedia.org/wiki/Static_single_assignment_form
この答えは、(a)一から現代のコンパイラでコンパイル作業および(bについて、その全部もしくお願いし、コンパイラの場合、このような、特定のローカル変数への登録--どめいんとうです。各可変しながら、オプティマイザとして複数の変数は、あく使用される、ループ、その他。いくつかの変数は消える--などにより定;または、時には、温度変数の使用のスワップ.又計算には実際に使用します。のコンパイラが完備になると登録のためのもの異なる部品のコードのように機械で作成します。
の概念を抱にコンパイラは、どの変数のあるべきレジスタは、各ローカル変数マップに登録またはメモリの場所です。そうでしたがKernighan+リッチー博士の設計では、C言語ものではありません。
について制限できるかのアドレスの登録変数:明らかにな実施のアドレスの変数を開催して登録で入手できるかもしれませんが、いかにコンパイラは、裁量により無視するの登録-なぜこのルールです。できないのはなぜコンパイラでは無視し、登録すれば起こるのかとしてのC++).
再びとなっており、コンパイラです。元のK+Rコンパイラが構文解析し、局所変数の宣言し すぐ から割り当て、登録のためになる場合は、登録).その後進コンパイル表現の発光にアセンブラのためのそれぞれです。だができることを見つけることだったのアドレスの登録数が担当していた登録があったように扱うことを、今回の授業で出された課題は、一般的に不可逆的でした。すことができたし、エラーメッセージおよび停止を作成.
下線で表示される登録は本質的に廃止:
- C++コンパイラ完全に無視されま
- Cコンパイラでコンパイルを無視するものを除き一律の制限
&
-がん無視し-O0
できるように実際に結果を配分して求められます。At-O0んご関係コードについて速いる。
なので基本的にし、その結果について後方互換性のために、その一部の実装ではまだまだ十分とはいえませんが使用する"ヒント'.ったもので、書いていた実時間DSPコードで過ごすイベントカレンダーの時間のみ発生するコードできるのかを探ることとなります。多くの方法を変更するようにするためのコードで実行時に、これらのコンパイラでコンパイル作業があります。これは実際にかかる追加登録する者。
付録
外からは私の特別の定義'人',変数に &
(これはもちろん通常の意味での実現を目指します。
考え、以下のコード:
void
somefunc()
{
int h,w;
int i,j;
extern int pitch;
get_hw( &h,&w ); // get shape of array
for( int i = 0; i < h; i++ ){
for( int j = 0; j < w; j++ ){
Arr[i*pitch + j] = generate_func(i,j);
}
}
}
これが完全に無害化.も困りの場合は実行速度を考えます:のコンパイラのアドレス h
や w
へ get_hw
, その後、呼び出し generate_func
.このようなコンパイラを知っても何のその機能(一例)です。のコンパイラ 必要 このよう呼 generate_func
可変 h
または w
.これは完全に法的使用のポインタを渡す get_hw
またどこかでこいつとして利用で、どの範囲を含む h,w
まだ遊べて、読み込みまたは書き込みの方。
このようにコンパイラを保管しなければならな h
や w
メモリーのスタックになるものについては、事前にどのようにループします。そうでユークリックするとnhkサイトを離のループが効率的にな結果として(この例では、ある関数呼び出しの内部ループんでいただけない場合もあり多くの違いが考える場合がある 時には 呼び出して内側のループによっては、あることにしました。
もう一つの問題は、 generate_func
変更 pitch
, など i*pitch
ニーズに格別の時間による場合のみ i
変わります。
で保存して
void
somefunc()
{
int h0,w0;
int h,w;
int i,j;
extern int pitch;
int apit = pitch;
get_hw( &h0,&w0 ); // get shape of array
h= h0;
w= w0;
for( int i = 0; i < h; i++ ){
for( int j = 0; j < w; j++ ){
Arr[i*apit + j] = generate_func(i,j);
}
}
}
現在の変数 apit,h,w
すべての"安全化"が地元の人々に感して定義した上で、コンパイラについて確信できな変更されず機能します。と仮定してい ない 修正い generate_func
, をコードしていることを求める申立てをすることができます。
Jens Gustedtであるとの提案がなされ、利用には登録キーワードとしてのタグ付けにキー変数を使用の抑制 &
して、例えばによるその他の維持のコードではな影響が生成されたコードからコンパイラを測定することができるな &
なします。のための私立場に立って物事を考えることを &
カルスカラーにタイムクリティカルな領域をコードし、私は利用登録への停止をすることはちょっと隠出すことはできなかったが、ポイント(残念ながらうまくいきませんでC++のコンパイラまでを無視する'登録').
ちなみに、コード効率の最良の方法を関数の戻り値には、構造体:
struct hw { // this is what get_hw returns
int h,w;
};
void
somefunc()
{
int h,w;
int i,j;
struct hw hwval = get_hw(); // get shape of array
h = hwval.h;
w = hwval.w;
...
この場面倒を見てやるのが面倒、その生クリーナーコードの以前の例です。のstruct hw"では実際に返される二つのレジスタ(ほとんどの現代ABIsとにかく).とよのhwval'structをお使いのバージョンのオプティマイザが効果的に休憩で二つ'人' hwval.h
や hwval.w
, そのことを確認すると同等の h
や w
--で hwval
がなくなる。ないポインタが必要なために機能する他の機能のつのポインター;このようなものを二つの異なるスカラー戻り値です。このやすくなってC++で11● std::tie
や std::tuple
, ご利用いただくことができ法少ないレベル(やく書構造体定義.
2番目の例はCで無効です。 register
キーワードは何かを変更します(c)。変数の住所の取得を阻害するのは、この目的のためだけにあります。したがって、口頭で「登録」という名前をとらないでください。それは誤った呼び名ですが、その定義に固執してください。
そのC ++は無視しているようです register
, 、まあ、彼らはその理由を持っているに違いありませんが、一方の有効なコードが他方に無効である場合、これらの微妙な違いの1つを再び見つけるのは少し悲しいと思います。