コンパむル。 7レゞスタ割り圓お

ファむル名の長さは無限であり、無限は255文字に蚭定されたす。
-ピヌタヌコリン゜ンUnixファむルシステム

したがっお、p-codeのプログラムがあり、無制限の数のレゞスタ぀たり255を自由に䜿甚できたす。 実際のプロセッサのレゞスタの数ははるかに少ない4぀ず仮定。 どうする

さらに投皿

  1. Pコヌド解析
  2. ラむフタむム
  3. 実装
  4. シンプルな最適化
  5. バヌゞョン分割
  6. メモリを操䜜する
  7. どうしたの

Pコヌド解析


最埌の投皿では、コンパむルされたプログラムのダンプがありたした。 40のチヌムは簡単に解読できたす。
 00 mov r01,0
 01 mov r02、0x3e8
 02゚コヌ0x126
 03 echo r01
 04゚コヌ0xa0
 05゚コヌr02
 06゚コヌ0xa7
 07 le r03、r01、r02
 08 jz r03、+ 0x1d= 0x26
 09 r04、r01、r02を远加
 0a mov r05、2
 0b div r06、r04、r05
 0c゚コヌ0x162
 0d゚コヌr06
 0e゚コヌ0xcc
 0f入力r07
 10 mov r08、1
 11 eq r09、r07、r08
 12 jz r09、+ 4= 0x17
 13 mov r0a、1
 14サブr0b、r06、r0a
 15 r02、r0b、0を远加
 16 jz 0、+ e= 0x25
 17 mov r0c、2
 18 eq r0d、r07、r0c
 19 jz r0d、+ 4= 0x1e
 1a mov r0e、1
 1b r0f、r06、r0eを远加
 1c r01、r0f、0を远加
 1d jz 0、+ 7= 0x25
 1e mov r10、3
 1f eq r11、r07、r10
 20 jz r11、+ 3= 0x24
 21゚コヌ0x146
 22 hlt
 23 jz 0、+ 1= 0x25
 24゚コヌ0x16a
 25 jz 0、-0x1f= 7
 26゚コヌ0xff
 27 hlt

すべおのレゞスタが同時に䜿甚されるわけではないこずがわかりたす。ほずんどのレゞスタは、次のコマンドにのみ必芁な䞭間倀を保存したす。 たずえば、 r03コマンド07-08でのみ䜿甚され、 r03チヌム1b-1cのみ䜿甚されたす。 これは、栌玍された倀に同じ物理レゞスタを䜿甚するこずに障害がないこずを意味したす07-08コマンドでは1぀の倀に䜿甚され、 1b-1cでは別の倀に䜿甚されたす。 異なる倀に同時に䜿甚されない2぀のレゞスタは、1぀の物理レゞスタに結合できたす。

「異なる倀に同時に䜿甚される」の定矩にはわずかな埮劙さがありたす。 第䞀に、レゞスタの寿呜を最初の蚀及から最埌の蚀及たでの単なる間隔ず芋なすこずは䞍可胜です。コヌドには、たずえば、すべおの方向のゞャンプを含めるこずができたす。
    ...
 10 mov r、42
 11 jz 0、20
 12゚コヌr
    ...
 20 ...
    ...
 30 jz 0、12
    ...

rはチヌム10〜12でのみ蚀及されおいたすが、チヌム20でも暗黙的に䜿甚されたす。これは、その埌、12に戻るこずができるためです。 したがっお、 rはコマンド20で䜿甚される他のレゞスタず組み合わせるこずはできたせん。

第二の埮劙さ
    ...
 10 mov r、42
 11゚コヌr
    ...
 20 ...
    ...
 30 mov r、24
 31゚コヌr
    ...

コマンド20では、レゞスタrは実際には䜿甚されおいたせんが、その前10-11ずその埌30-31の䞡方に蚀及されおいたす。 次の䜿甚31の前に倀が新しい方法で割り圓おられるため、他の倀を保存できたす。 基本的に、倀が異なるため、10-11ず31-31でrに異なる物理レゞスタを割り圓おるこずもできたす。 ただし、このためには、 r 2぀の独立した「バヌゞョン」 rこずを確認し、それらを個別に凊理する必芁がありたす。

ラむフタむム


レゞスタが䜿甚されるコマンドのセットを繰り返し決定するこずができたす。
各コマンドに぀いお、「入力時」および「出力時」に必芁なレゞスタのセットを決定し、これらのセットがむンストヌルされるたで3぀のルヌルを適甚したす。 プログラムで埗られるものは次のずおりです。
             反埩数1入力/出力2入力/出力3入力/出力4入力/出力...合蚈入力/出力
 00 mov r01,0 / r01
 01 mov r02、0x3e8 / r01 r01 / r01、r02
 02 echo 0x126 / r01 r01 / r01 r01 / r01 r01、r02 / r01、r02
 03 echo r01 r01 / r01 / r01 / r01 / r02 r01、r02 / r01、r02
 04 echo 0xa0 / r02 r02 / r02 r02 / r02 r01、r02 / r01、r02
 05 echo r02 r02 / r02 / r02 / r02 / r01、r02 r01、r02 / r01、r02
 06 echo 0xa7 / r01、r02 r01、r02 / r01、r02 r01、r02 / r01、r02 r01、r02 / r01、r02
 07 le r03、r01、r02 r01、r02 / r01、r02 / r03 r01、r02 / r03 r01、r02 / r01、r02、r03 r01、r02 / r01、r02、r03
 08 jz r03、+ 0x1d= 0x26r03 / r03 / r01、r02 r01、r02、r03 / r01、r02 r01、r02、r03 / r01、r02 r01、r02、r03 / r01、r02
 09 r04、r01、r02 r01、r02 / r01、r02 / r01、r02 / r01、r02 / r04 r01、r02 / r01、r02、r04を远加
 0a mov r05、2 / r04、r05 r04 / r04、r05 r04 / r04、r05 r01、r02、r04 / r01、r02、r04、r05
 0b div r06、r04、r05 r04、r05 / r04、r05 / r04、r05 / r04、r05 / r06 r01、r02、r04、r05 / r01、r02、r06
 0c゚コヌ0x162 / r06 r06 / r06 r06 / r06 r01、r02、r06 / r01、r02、r06
 0d゚コヌr06 r06 / r06 / r06 / r06 / r01、r02、r06 / r01、r02、r06
 0e゚コヌ0xcc r01、r02、r06 / r01、r02、r06
 0f入力r07 / r07 r01、r02、r06 / r01、r02、r06、r07
 10 mov r08、1 / r07、r08 r07 / r07、r08 r07 / r07、r08 r01、r02、r06、r07 / r01、r02、r06、r07、r08
 11 eq r09、r07、r08 r07、r08 / r07、r08 / r09 r07、r08 / r09 r07、r08 / r09 r01、r02、r06、r07、r08 / r01、r02、r06、r07、r09
 12 jz r09、+ 4= 0x17r09 / r09 / r09 / r09 / r06、r07 r01、r02、r06、r07、r09 / r01、r02、r06、r07
 13 mov r0a、1 / r06、r0a r06 / r06、r0a r06 / r06、r0a r01、r06 / r01、r06、r0a
 14サブr0b、r06、r0a r06、r0a / r06、r0a / r0b r06、r0a / r0b r06、r0a / r0b r01、r06、r0a / r01、r0b
 15 r02、r0b、0 r0b / r0b / r0b / r0b / r01、r0b / r01、r02を远加
 16 jz 0、+ e= 0x25r01、r02 / r01、r02
 17 mov r0c、2 / r07、r0c r07 / r07、r0c r07 / r07、r0c r01、r02、r06、r07 / r01、r02、r06、r07、r0c
 18 eq r0d、r07、r0c r07、r0c / r07、r0c / r0d r07、r0c / r0d r07、r0c / r0d r01、r02、r06、r07、r0c / r01、r02、r06、r07、r0d
 19 jz r0d、+ 4= 0x1er0d / r0d / r0d / r0d / r06、r07 r01、r02、r06、r07、r0d / r01、r02、r06、r07
 1a mov r0e、1 / r06、r0e r06 / r06、r0e r06 / r06、r0e r02、r06 / r02、r06、r0e
 1b r0f、r06、r0e r06、r0e / r06、r0e / r0f r06、r0e / r0f r06、r0e / r0f r02、r06、r0e / r02、r0fを远加
 1c r01、r0f、0 r0f / r0f / r0f / r0f / r02、r0f / r01、r02を远加
 1d jz 0、+ 7= 0x25/ r01、r02 r01、r02 / r01、r02
 1e mov r10、3 / r07、r10 r07 / r07、r10 r07 / r07、r10 r01、r02、r07 / r01、r02、r07、r10
 1f eq r11、r07、r10 r07、r10 / r07、r10 / r11 r07、r10 / r11 r07、r10 / r11 r01、r02、r07、r10 / r01、r02、r11
 20 jz r11、+ 3= 0x24r11 / r11 / r11 / r11 / r01、r02、r11 / r01、r02
 21゚コヌ0x146
 22 hlt
 23 jz 0、+ 1= 0x25/ r01、r02 r01、r02 / r01、r02
 24゚コヌ0x16a / r01、r02 r01、r02 / r01、r02
 25 jz 0、-0x1f= 7/ r01、r02 r01、r02 / r01、r02 r01、r02 / r01、r02 r01、r02 / r01、r02
 26゚コヌ0xff
 27 hlt
個々の反埩に぀いお
  1. 各コマンドの入力時-コマンドが䜿甚するレゞスタ。 ルヌル番号1
  2. 各コマンドの出力で、そこから到達可胜なコマンドの入力をコピヌしたす。 ルヌル番号2
  3. 各コマンドの入力で、コマンドによっお蚘録されたレゞスタを陀いお、その出力をコピヌしたす。 ルヌル番号3
  4. 各コマンドの出力で、そこから到達可胜なコマンドの入力をコピヌしたす。 ルヌル番号2
さらに、ルヌルNo. 2ずNo. 3が亀互に繰り返されたす。

数回の反埩の埌、「レゞスタの存続可胜性」の最終マヌクアップを取埗したす。これは、各チヌムのニヌズを登録したす。 次に、pコヌドのレゞスタの名前を物理的なものに倉曎できたす。たずえば、「貪欲」です。「最も必芁な」レゞスタコマンドの最倧数が必芁を取埗したす。 察応するチヌムでただ䜿甚されおいない物理的な名前に倉曎したす。 繰り返したす。
可胜な限り十分な物理レゞスタヌを提䟛する最適な遞択は、培底的な怜玢によっおのみ可胜ですこのタスクは、「最小の色でグラフを着色する」ず同等であり、数孊者はそれに察する有効な解決策はないず考えおいたす。 「貪欲な」アプロヌチは、最悪の可胜性ではありたせん。

次のpレゞスタに物理的な空きがない堎合はどうすればよいですか たずえば、このコヌドには、それぞれ5぀のpレゞスタを䜿甚するコマンド10〜12、17〜19がありたす。少なくずも1぀に十分なスペヌスがありたせん。 メモリ内に保持する必芁があるこずは明らかです。
しかし、レゞスタヌに「あなたは䞍運だ、あなたはメモリ内に保持されたす」ず蚀うだけでは十分ではありたせん。私たちのpコヌドの指瀺はメモリからの倀ずほずんどの実際のプロセッサのコマンドを凊理する方法を知りたせん。 䜿甚時にメモリから倀を取埗しお登録する必芁がありたす。 どれだけ 実際、すべおのレゞスタがビゞヌであるため、メモリ内の倀を削陀したためです

この時点で、私が孊んだ理論は掚枬ずヒュヌリスティックで終わりたす。 私は、十分なレゞスタがないチヌムの前に、「䜙分なレゞスタをメモリに流し蟌む」こずを決め、その埌、メモリからそれらを読み戻したした。 したがっお、泚ぎ出されたレゞスタの「存続期間」では、このコマンドの堎所に穎が衚瀺され、この穎で他の目的に解攟されたレゞスタを䜿甚できたす。

コヌドを「抌しのけお」、読み取り/曞き蟌みコマンドをメモリに挿入できるようにするには、䞭間圢匏を少し倉曎する必芁がありたすこれはリンクリストになり、ゞャンプタヌゲットはオフセットずしおではなく、ポむンタヌずしお保存されたすリストを有向グラフに倉換。 pコヌドの生成䞭に、 バックパッチのために、むンデックスでチヌムを参照する必芁がありたす。 このため、個別の倉䜍ベクトルがありたす。
typedef std::list< struct commandn>::iterator pcommandn;
struct commandn {
command cmd;
pcommandn tgt; //
commandn( const command& cmd) : cmd(cmd), tgt( NULL ) {}
};

std::list<commandn> pcode;
std::vector<pcommandn> pcodeidx;

inline int emit( const command& cmd) {
pcodeidx.push_back(pcode.insert(pcode.end(), commandn(cmd)));
return pcode.size()- 1 ;
}
inline int emit(command::opcodes opcode, regnum dest, regnum src1, regnum src2) {
return emit(command(opcode, dest, src1, src2));
}
inline int emit(command::opcodes opcode, regnum dest, short imm) {
return emit(command(opcode, dest, imm));
}
inline void fix( int index, command::opcodes opcode, regnum dest, short imm) {
*pcodeidx[index] = commandn(command(opcode, dest, imm));
}

ゞャンプの目暙ぞのポむンタは、pコヌドの生成䞭に蚘入するのが困難です。ゞャンプを䜜成する時点では、タヌゲットむンデックスは既にわかっおいたすが、どのチヌムが存圚するかはただ䞍明です。 ただ䜜成されおいないチヌムにポむンタを眮くこずには問題がありたす。 したがっお、pコヌドの解析ず生成の終了埌、生成されたコヌドをバむパスしおゞャンプポむンタヌを配眮したす。 次に、レゞスタの割り圓おに必芁なすべおの䜜業を行いたす。 最埌に、pコヌドをファむルにダンプする前に、それを「シリアル化」する必芁がありたす-各コマンドの最終むンデックスを決定し、ゞャンプオフセットを入力したす。 このために、 int index;フィヌルドをstruct commandnに远加しint index;

int main() {
yyparse();
//
for ( int i= 0 ; i<pcode.size(); i++)
if (pcodeidx[i]->cmd.opcode==command::jz)
pcodeidx[i]->tgt = pcodeidx[i+ 1 +pcodeidx[i]->cmd.imm];

// /
// ...

// /
int index = 0 ;
foreach(i, pcode) i->index = index++;
foreach(i, pcode)
if (i->cmd.opcode==command::jz)
i->cmd.imm = i->tgt->index-i->index- 1 ;

// -
// ...
}

ポむントは小さい「凊理/最適化」を実装したす。 ご想像のずおり、ほずんどのコンパむラコヌドが必芁です。

実装


レゞスタを割り圓おるために、各コマンドに察しお struct commandn盎接入力および出力で有効なレゞスタのセットを保存したす。ラむフタむムを決定するためのpコヌドレゞスタ。 および個別に-実際の目的ずの互換性を刀断するための物理レゞスタ。
typedef std::set<regnum> aliveset;
enum physreg { firstp = 1 , lastp = 4 };
typedef std::set<physreg> alivesetp;
struct commandn {
// ...
aliveset onenter, onexit; // liveness check
alivesetp onenterp, onexitp; // phys reg allocation
// ...
};

もう1぀の䟿利なこずは、コマンドで䜿甚されるレゞスタをオペコヌドで刀断するこずです。
inline bool hasnext(pcommandn cmd) {
return (cmd->cmd.opcode!=command::hlt &&!
(cmd->cmd.opcode==command::jz && !cmd->cmd.dest));
}
inline bool has2src(pcommandn cmd) {
return cmd->cmd.opcode>=command::add;
}
inline bool writesdest(pcommandn cmd) {
return cmd->cmd.opcode>=command::mov;
}
inline bool readsdest(pcommandn cmd) {
return cmd->cmd.opcode>=command::store &&
cmd->cmd.opcode<=command::echo;
}

その埌、䞊の図に埓っお、各コマンドに぀いお、入力ず出力で必芁なレゞスタのセットを蚈算したす。 同時に、貪欲なアポむントメントのために、各レゞスタが必芁な回数をカりントしたす。
std::vector< int > liveness() { // returns usecount
std::vector< int > usecount(lastreg+ 1 );
bool changed = false ;
// rule 1
foreach(i, pcode) {
i->onenter = i->onexit = aliveset();
if (has2src(i)) {
if (i->cmd.src1) {
usecount[i->cmd.src1]++;
i->onenter.insert(i->cmd.src1);
}
if (i->cmd.src2) {
usecount[i->cmd.src2]++;
i->onenter.insert(i->cmd.src2);
}
} else if (readsdest(i) && i->cmd.dest) {
usecount[i->cmd.dest]++;
i->onenter.insert(i->cmd.dest);
}
if (i->onenter.size())
changed = true ;
}
while (changed) {
changed = false ;
foreach2(i, pcode, next) {
int oldsize = i->onenter.size()+i->onexit.size();
// rule 2 (next command)
if (hasnext(i))
i->onexit.insert(next->onenter.begin(), next->onenter.end());
// rule 2 (jmp target)
if (i->cmd.opcode==command::jz)
i->onexit.insert(i->tgt->onenter.begin(), i->tgt->onenter.end());
// rule 3
i->onenter.insert(i->onexit.begin(), i->onexit.end());
if (writesdest(i))
i->onenter.erase(i->cmd.dest);

if (i->onenter.size()+i->onexit.size() != oldsize)
changed = true ;
}
}
return usecount;
}

foreach2マクロをforeach2するず、ルヌプの本文の珟圚の芁玠ず次の芁玠を確認できたす。
#define foreach2(i, list, next) typedef typeof(list) TOKENPASTE2(T, __LINE__ ); \
        for (TOKENPASTE2(T, __LINE__ )::iterator next = list.begin(), i=next++; i != list.end(); i=next, next++)

原則ずしお、チヌムが「䞊から䞋」ではなく「䞋から䞊」にバむパスされない堎合は、各チヌムから前のチヌムに「広がる」必芁があるため、反埩の総数を枛らすこずができたす。 コヌドを簡玠化するためにトップダりンバむパスが遞択されたした。 最悪の堎合、長さNのコマンドのコヌドでは、2 N回の反埩が必芁になりたす反埩ごずに、コマンドの半分の距離たで必芁になりたす。 test.jskには35回の反埩で十分でした。ゞャンプするず距離が短くなるためです。

最埌に、なぜこれをすべお行ったのか
// / :
while ( 1 ) { //

//
// ...

std::vector< int > usecount = liveness();
std::vector<physreg> physmap(lastreg+ 1 );
foreach(i, pcode)
i->onenterp = i->onexitp = alivesetp();
while ( 1 ) {
//
std::vector< int >::iterator max = std::max_element(usecount.begin(), usecount.end());
if (!*max) break ; //
*max = 0 ;
regnum r = max - usecount.begin();
// . - №r
std::vector< int > collisions(lastp); // 0-based
for (physreg p=firstp; p<=lastp; p=(physreg)(p+ 1 )) { // error: ISO C++ forbids incrementing an enum
foreach(i, pcode) {
if (contains(i->onenter, r) && contains(i->onenterp, p))
collisions[p- 1 ]++;
if (contains(i->onexit, r) && contains(i->onexitp, p))
collisions[p- 1 ]++;
}
if (collisions[p- 1 ]) continue ; // .
physmap[r] = p;
// - №r №p
foreach(i, pcode) {
if (contains(i->onenter, r))
i->onenterp.insert(p);
if (contains(i->onexit, r))
i->onexitp.insert(p);
}
break ; //
}
if (physmap[r]) continue ; //
// . - №r
// : ,
foreach2(i, pcode, next)
if ((i->cmd.opcode!=command::load && i->cmd.opcode!=command::store) &&
((contains(i->onenter, r) && i->onenterp.size()==lastp) ||
(contains(i->onexit, r) && i->onexitp.size()==lastp))) {
// , ,
// ...
goto afterspill;
}
// : ?
yyerror( "don't know how to proceed" );
}
// ;
foreach(i, pcode) {
i->cmd.dest = physmap[i->cmd.dest];
if (has2src(i)) {
i->cmd.src1 = physmap[i->cmd.src1];
i->cmd.src2 = physmap[i->cmd.src2];
}
}
break ;

afterspill : // - " "
}

それでもレゞスタをどのように泚ぐかに぀いお正確には掘り䞋げおいたせんが、䞊蚘のコヌドには倚くの問題があるこずに蚀及する䟡倀がありたす。 第䞀に、ヒュヌリスティックは最も単玔な堎合にのみ機胜したす。 より䞀般的な状況
    ...
 10にはr1、r2、r3、r4が必芁
    ...
 20にはr1、r3、r4、r5が必芁
    ...
 30にはr2、r3、r4、r5が必芁
    ...
すべおの物理r5がr1,r2,r3,r4費やされたため、 r5には物理r5がありたせん。 ただし、5぀のレゞスタすべおが同時に必芁な単䞀のコマンドはありたせん。

より難しい問題は、すべおの流出が無駄なstore/loadペアでプログラムを詰たらせるこずであり、それらをきれいにする必芁がありたす-さもなければ、それらで䜿甚されるレゞスタは十分な「ノむズ」を䜜成し、䜕もレゞスタがなくなりたす。

別のアむデア流出に起因するラむフタむムの穎は、元のpレゞスタを異なる物理レゞスタに配眮できる2぀の独立した「バヌゞョン」に分割する可胜性がありたす。 システムの柔軟性のために、バヌゞョン分割を実装する䟡倀がありたす。倖郚サむクルを繰り返すたびに、レゞスタの寿呜が短くなり、物理的なレゞスタの分散がより簡単になりたす。

そしお、䜙分なloadずstoreを削陀しようずしたので、他の䜙分なコマンドも同時に削陀したす。 pコヌドの操䜜には他の副䜜甚がないこずを考慮するず、結果でレゞスタヌを蚈算する以倖に、出力で結果が生きおいないコマンドを削陀できたす。 たぶんプログラマヌは倀を未䜿甚の倉数に割り圓おるか、 (2+2);ような挔算子を䜿甚したした(2+2);
行内のすべおを削陀したす。そのため、ヒヌプぞの到達䞍胜コヌドたずえばhltの埌のjzを削陀し、ゞャンプのチェヌンを圧瞮したすゞャンプを1回のゞャンプのゞャンプに眮き換えたす。

コマンドを削陀しお、その䞊のすべおのゞャンプを怜玢および修正しないようにするには、コマンドをnopに眮き換えるず䟿利です。 次のコマンド jz r00, +0 に無条件ゞャンプを割り圓おたす。 利点は、次の反埩で、 nopのゞャンプがチェヌンに沿っお圧瞮され、 nop自䜓が到達䞍胜なチヌムずしおクリヌンアップされるこずです。

シンプルな最適化


到達䞍胜なチヌムを怜出するために、詳现なバむパスを䜿甚したす。 どのコマンドが既にバむパスされおいるかを远跡するには、 struct commandn int reachcnt;新しいint reachcnt;フィヌルドを远加しint reachcnt;
ブヌル倀フィヌルドで十分です。 しかし、各チヌムの「゚ントリヌの皋床」は、い぀か圹に立぀こずがわかるかもしれたせん。
void markReachable(pcommandn cmd) {
while (!cmd->reachcnt++) {
if (cmd->cmd.opcode==command::jz)
markReachable(cmd->tgt);
if (hasnext(cmd))
cmd++;
else
break ;
}
}

void nopOut(pcommandn cmd) {
pcommandn next = cmd; next++;
cmd->cmd = command(command::jz, 0 , 0 );
cmd->tgt = next;
}

void simpleopt() {
//
foreach(i, pcode)
if (i->cmd.opcode==command::jz)
while (i->tgt->cmd.opcode==command::jz && !i->tgt->cmd.dest)
i->tgt = i->tgt->tgt;
//
foreach(i, pcode) i->reachcnt = 0 ;
markReachable(pcode.begin());
foreach2(i, pcode, next)
if (!i->reachcnt)
pcode.erase(i);
// nop:
foreach2(i, pcode, next)
if (i->cmd.opcode==command::jz &&
!i->cmd.dest && i->tgt==next)
pcode.erase(i);
}

// :
simpleopt();
//
liveness();
bool changed = false ;
foreach(i, pcode)
if (writesdest(i) && !contains(i->onexit, i->cmd.dest)) {
nopOut(i);
changed = true ;
}
if (changed) continue ;
//
// ...


バヌゞョン分割


レゞスタの「必芁間隔」にギャップがある堎合、぀たり ある呜什セットの倀が別のセットに䟵入するこずはありたせん。぀たり、レゞスタを、より短い寿呜のレゞスタのペアに分割するこずができたす。
別のグラフアルゎリズムは、ラむフタむムから䞍連続なフラグメントを抜出するのに圹立ちたす接続されたコンポヌネントの怜玢。
私たちは、コヌドの有効性よりもコヌドの明快さを远求しおいたす。 したがっお、pvg内にpvgがあり、グラフ党䜓を走査するこずでコンポヌネントがマヌゞされたす。

struct commandnは、今日の最埌の別のフィヌルドが必芁ですstd::map<regnum,regnum> version;
その䞭に、各察応コマンド「p-register-> version number」を保存したす。
コマンドで倧文字ず小文字が䜿甚されおいるが、 versionただ䞀臎しおいない堎合、回避策はただこのコマンドに到達しおいたせん。
倖郚pvgの堎合、新しいフィヌルドを開始しないために、 markReachable()ず同じreachcntを䜿甚したす
std::map<regnum,regnum> renameto; //

void versionizeReg(pcommandn cmd, regnum r, regnum rv) { // r rv
while (!contains(cmd- > version, r) &&
(contains(cmd- > onenter, r) || contains(cmd- > onexit, r))) {
//
cmd- > version[r] = renameto[rv];
if (cmd- > cmd.dest==r) cmd- > cmd.dest=renameto[rv];
if (has2src(cmd)) {
if (cmd- > cmd.src1==r) cmd- > cmd.src 1 =renameto[rv];
if (cmd- > cmd.src2==r) cmd- > cmd.src 2 =renameto[rv];
}
if (!contains(cmd- > onexit,r)) return ; //
if (cmd- > cmd.opcode==command::jz &&
contains(cmd- > tgt- > onenter, r)) {
versionizeReg(cmd- > tgt, r, rv);
}
if (hasnext(cmd)) {
cmd++;
if (contains(cmd- > onenter, r))
continue ;
}
return ; //
}
// ?
if (contains(cmd- > version, r) && cmd- > version[r]!=renameto[rv]) {
// renameto[rv] cmd->version[r]
regnum from = std::max(cmd- > version[r], renameto[rv]),
to = std::min(cmd- > version[r], renameto[rv]);
renameto[from] = to;
foreach(i, pcode) {
if (i- > cmd.dest==from) i- > cmd.dest = to;
if (has2src(i)) {
if (i- > cmd.src1==from) i- > cmd.src 1 = to;
if (i- > cmd.src2==from) i- > cmd.src 2 = to;
}
if (contains(i- > version, r) && i- > version[r]==from)
i- > version[r] = to;
}
}
}

void versionize(pcommandn cmd) {
while (!cmd- > reachcnt++) {
// versionize registers that live on exit
foreach(r, cmd- > onexit)
if (!contains(cmd- > version, *r)) {
regnum rv = newreg();
renameto[rv] = rv;
versionizeReg(cmd, *r, rv);
}
if (cmd- > cmd.opcode==command::jz)
versionize(cmd- > tgt);
if (hasnext(cmd))
cmd++;
else
break ;
}
}

// :
foreach(i, pcode) {
i- > version.clear();
i- > reachcnt = 0 ;
}
renameto.clear();
int lastbasereg = lastreg;
versionize(pcode.begin());
// , 1
foreach(i, pcode) {
if (i- > cmd.dest) i- > cmd.dest-=lastbasereg;
if (has2src(i)) {
if (i- > cmd.src1) i- > cmd.src 1 -=lastbasereg;
if (i- > cmd.src2) i- > cmd.src 2 -=lastbasereg;
}
}
lastreg -= lastbasereg;

実際のレゞスタヌの泚入を実装するこずは残っおいたす。 ヒュヌリスティックを指定したす生きおいるが、コマンドで䜿甚されおいないレゞスタを泚ぎ出すこずができたす。
regnum spillable(pcommandn cmd) { // ,
aliveset alive;
alive.insert(cmd->onenter.begin(), cmd->onenter.end());
alive.insert(cmd->onexit.begin(), cmd->onexit.end());
alive.erase(cmd->cmd.dest);
if (has2src(cmd)) {
alive.erase(cmd->cmd.src1);
alive.erase(cmd->cmd.src2);
}
if (!alive.size()) return 0 ;
return *alive.begin();
}

別のmapは、レゞスタのすべおの流出が同じ堎所に収たるように、レゞスタ番号によっおそのコピヌの「アドレス」をメモリに保存したす。 アドレスは、最初の流出時にのみレゞスタに割り圓おられたす。
// spill slots
std::map<regnum, int > spill;
int lastspill = 0 ;

void spillAt(pcommandn cmd, regnum victim, pcommandn next) {
// /
int spslot = spill[victim];
if (!spslot) {
spslot = ++lastspill;
spill[victim] = spslot;
}
// store load
pcommandn me = pcode.insert(next, *cmd);
cmd->cmd = command(command::store, victim, spslot);
commandn load(command(command::load, victim, spslot));
if (hasnext(me))
pcode.insert(next, load);
if (me->cmd.opcode==command::jz)
me->tgt = pcode.insert(me->tgt, load);
}

チヌムはstore-cmd-loadトリプルに眮き換えられるため、チヌムぞの既存のゞャンプは、その前に挿入されたstoreにゞャンプする必芁がありたす。 コヌド党䜓でこのようなゞャンプを探しないようにcmd 次のコマンドでcmdコピヌを挿入し、 元のコマンドをstore眮き換えたす。
同様に、コマンドが条件付きゞャンプの堎合、䞡方の結果でload実行する必芁がありload 。 したがっお、次のコマンドで远加し、ゞャンプの目暙の前に挿入したす。

コマンドをあるコマンドから別のコマンドに移動する可胜性があるため、最初のツリヌ実装のように、 echoの行を「識別子」によっおコマンド自䜓にバむンドする必芁がありたす。コマンドむンデックスもポむンタも倉曎されたせん。

2番目のヒュヌリスティックすべおの物理レゞスタヌが占有されおいるチヌムを芋぀けるこずができなかった堎合、「最適な」物理レゞスタヌ既に割り圓おられおいるレゞスタヌずの衝突が最も少ないを取埗し、必芁な堎所で解攟したす。
// . - №r
regnum victim;
// 1: ,
foreach2(i, pcode, next)
if ((i->cmd.opcode!=command::load && i->cmd.opcode!=command::store) &&
((contains(i->onenter, r) && i->onenterp.size()==lastp) ||
(contains(i->onexit, r) && i->onexitp.size()==lastp))) {
victim = spillable(i);
assert(victim);
spillAt(i, victim, next);
goto afterspill;
}
// 2: " "
physreg bestfit = (physreg)(std::max_element(collisions.begin(), collisions.end())-collisions.begin()+ 1 );
foreach2(i, pcode, next)
if ((i->cmd.opcode!=command::load && i->cmd.opcode!=command::store) &&
((contains(i->onenter, r) && contains(i->onenterp, bestfit)) ||
(contains(i->onexit, r) && contains(i->onexitp, bestfit))) &&
(victim = spillable(i))) {
spillAt(i, victim, next);
goto afterspill;
}
yyerror( "don't know how to proceed" );

バヌゞョンを分割するずきは、スロット番号をコピヌするこずを忘れないでください同じレゞスタのすべおのバヌゞョンが1぀のスロットに保存されたす。次に、バヌゞョンの名前を倉曎するずきは、 spillを曎新するこずを忘れないでください。

コンパむラはほずんど機胜しおいたす。 最埌の問題は、無意味なloadずstore倧量に生成され、それらのために意味のあるコマンド甚のレゞスタがないこずです。

メモリを操䜜する


最初に、䞀郚のレゞスタがload/storeコマンドで排他的に䜿甚されるこずが刀明する堎合がありたす。
このようなコマンドは無意味です。レゞスタは垞に、読み取られたスロットず同じスロットに曞き蟌たれたす。
バヌゞョンを分割した埌にクリヌンアップを挿入したす。短いラむフタむムで倚くのレゞスタを取埗する必芁がありたす。
//
std::vector< bool > used(lastreg+ 1 );
foreach(i, pcode) {
if (writesdest(i) && i->cmd.opcode!=command::load)
used[i->cmd.dest] = true ;
if (readsdest(i) && i->cmd.opcode!=command::store)
used[i->cmd.dest] = true ;
if (has2src(i)) {
used[i->cmd.src1] = true ;
used[i->cmd.src2] = true ;
}
}
foreach(i, pcode)
if ((i->cmd.opcode==command::load && !used[i->cmd.dest]) ||
(i->cmd.opcode==command::store && !used[i->cmd.dest]))
nopOut(i);

第二に、同じloadチェヌンがある堎合、読み取りレゞスタが生きおいないため、最埌のチェヌン以倖はすべお削陀されたす。 しかし、同じstoreチェヌンがある堎合、それらを削陀するには、䜕か新しいものを考え出す必芁がありたす。 このアルゎリズムは、レゞスタの必芁性の反埩定矩に非垞に䌌おいたすが、「保存しない」を定矩しお、すでに保存されおいるレゞスタのすべおの保存をクリアしたす。このアルゎリズムは、配垃の方向のみが前のアルゎリズムず異なるこずがわかりたす。必芁性は各チヌムから前のチヌムにたで拡匵され、非保存は次のチヌムに拡匵されたす。
䞀般に、プログラムの各ポむントでレゞスタのセットを蚈算するための反埩アルゎリズムは、 デヌタフロヌ分析ず呌ばれ、2぀の名前を陀いお、他の倚くの最適化に䜿甚されたす。

void unsaved() {
bool changed = false ;
// rule 1
foreach(i, pcode) {
i->onenter = i->onexit = aliveset();
if (writesdest(i)) {
i->onexit.insert(i->cmd.dest);
changed = true ;
}
}
while (changed) {
changed = false ;
foreach2(i, pcode, next) {
int oldsize = i->onenter.size()+i->onexit.size();
// rule 2 (next command)
if (hasnext(i))
next->onenter.insert(i->onexit.begin(), i->onexit.end());
// rule 2 (jmp target)
if (i->cmd.opcode==command::jz)
i->tgt->onenter.insert(i->onexit.begin(), i->onexit.end());
// rule 3
i->onexit.insert(i->onenter.begin(), i->onenter.end());
if (i->cmd.opcode==command::store)
i->onexit.erase(i->cmd.dest);

if (i->onenter.size()+i->onexit.size() != oldsize)
changed = true ;
}
}
}

afterspill : // - " "
unsaved();
foreach(i, pcode)
if (i->cmd.opcode==command::store && !contains(i->onenter, i->cmd.dest))
nopOut(i);
}


どうしたの


コンパむラヌ党䜓 tyomitch.net.ru/jsk.y.regs.html
5䞇行のうち、解析の5分の1だけが属したす。 実際、他のコンパむラの人件費ず比范しお、パヌサヌは面癜くない些现なこずのようです。 しかし、これは、パヌサヌの䜜成が骚に吞い蟌たれ、準備ができおいるものしか䜿甚できないためです。 䞀方、コヌドを凊理するずき、フィクションの䜙地がありたす。

レゞスタの割り圓おず最初の最適化の結果、生成されたコヌドはいく぀かのコマンドのみで拡匵されたした。
 00 mov r01,0
 01 mov r02、0x3e8
 02゚コヌ0x12e
 03 echo r01
 04゚コヌ0xa8
 05゚コヌr02
 06゚コヌ0xaf
 07 le r03、r01、r02
 08 jz r03、+ 0x1f= 0x28
 09 r03、r01、r02を远加
 0a mov r04、2
 0b div r03、r03、r04
 0c゚コヌ0x16a
 0d゚コヌr03
 0e゚コヌ0xd4
 0f入力r04
 10店舗r01、1
 11 mov r01、1
 12 eq r01、r04、r01
 13 jz r01、+ 5= 0x19
 14ロヌドr01、1
 15 mov r02、1
 16サブr02、r03、r02
 17 r02、r02、0を远加
 18 jz 0、-0x12= 0x7
 19 mov r01、2
 1a eq r01、r04、r01
 1b jz r01、+ 4= 0x20
 1c mov r01、1
 1d r01、r03、r01を远加
 1e r01、r01、0を远加
 1f jz 0、-0x19= 0x7
 20ロヌドr01、1
 21 mov r03、3
 22 eq r03、r04、r03
 23 jz r03、+ 2= 0x26
 24゚コヌ0x14e
 25 hlt
 26゚コヌ0x172
 27 jz 0、-0x21= 7
 28゚コヌ0x107
 29 hlt

ただし、䜿甚されるレゞスタは4぀だけなので、このpコヌドはおそらく実際のプロセッサのマシンコヌドに倉換されたす。

8月も継続したす。䌑暇に行くので、この投皿を空枯から盎接远加したす。
みなさん玠敵な倏を

Source: https://habr.com/ru/post/J99595/


All Articles