1

バックストーリー: 私は、簡略化されたアセンブリのようなテキストを取り、それを 32 ビット命令に変換するおもちゃのコンパイラに取り組んでいます。

32 ビット命令への変換は正しく行われていますが、結果を出力しようとすると問題が発生します。具体的には、さまざまなセグメントを引き出すために私が書いたマクロは、説明できない方法で値をマングリングしているようです。

次のループで問題が発生します (ビット操作はマクロに保存されますが、デバッグを簡単にするために拡張しました)。

while(pc < num_insts)
{
   printf("full: %x | op: %x | rs: %x | rt: %x | rd: %x | imm: %x\n",
      inst_mem[pc],
      ((inst_mem[pc] >> 26) & 0x0000003F),
      ((inst_mem[pc] >> 21) & 0x0000003F),
      ((inst_mem[pc] >> 16) & 0x0000003F),
      ((inst_mem[pc] >> 11) & 0x0000F800),
      (inst_mem[pc++] & 0x0000FFFF));
}

以下を出力します。

full: 20450008 | op: 8 | rs: 2 | rt: 5 | rd: 0 | imm: 10
full: 0 | op: 0 | rs: 0 | rt: 0 | rd: 0 | imm: 8

これらの行の正しい値は次のとおりです。

full: 20450008 | op: 8 | rs: 1 | rt: 4 | rd: 0 | imm: 16
full: 20240010 | op: 8 | rs: 2 | rt: 5 | rd: 0 | imm: 8

それをより単純なものに置き換えると

while(pc < num_insts)
{
   printf("full: %x", inst_mem[pc++]);
}

次に、期待どおりに各完全なアドレスが出力されます。これは、解析がすべて正常に機能していること、およびマクロが解析後にアドレスから値を引き出して、本来あるべきではないものでジャックしていることを意味します。それが何であるはわかりません。

inst_mem[]値が含まれてint32_tおり、すべてのシフトが 32 未満であるため、文書化されていないシフト動作の問題ではありません。

誰かが正しい方向にナッジを提供できれば、私は感謝しています. 私はアイデアが不足しています。

4

2 に答える 2

3

関数の引数の評価順序は、「最初の引数が最初」である必要はありません。pc++したがって、他の関数の引数が を使用する場合のようにインクリメントを持つことは非常に危険pcです。その後に変更pc++pcてインクリメントします。pc

while(pc < num_insts)
{
   printf("full: %x | op: %x | rs: %x | rt: %x | rd: %x | imm: %x\n",
      inst_mem[pc],
      ((inst_mem[pc] >> 26) & 0x0000003F),
      ((inst_mem[pc] >> 21) & 0x0000003F),
      ((inst_mem[pc] >> 16) & 0x0000003F),
      ((inst_mem[pc] >> 11) & 0x0000F800),
      (inst_mem[pc] & 0x0000FFFF));
   pc++;
}
于 2013-09-22T00:32:40.600 に答える
1

プログラムの動作は未規定未定義です。関数引数の評価の順序は、C99 ドラフト標準セクションの6.5.2.2 関数呼び出しの段落10を見ると指定されていません(強調鉱山):

関数指定子、実引数、および実引数内の部分式の評価順序は指定されていませんが、実際の呼び出しの前にシーケンス ポイントがあります。

そのため、部分式が他の引数に対していつ実行されるかを判断できないため、いつインクリメントされるinst_mem[pc++] & 0x0000FFFFかわかりません。pc

セクションExpressionsの段落2を6.5 見ると (強調鉱山):

前のシーケンス ポイントと次のシーケンス ポイントの間で、オブジェクトは、式の評価によって最大 1 回変更された格納された値を持つ必要があります

脚注73では、未定義の動作の次の例を示します。

i = ++i + 1;
a[i++] = i;

そのため、シーケンス ポイント内でオブジェクトが変更された場合、以前の値は、保存する値を決定するためにのみ読み取ることができます。あなたのコードpcpc++式で変更されており、配列インデックスを決定するために何度か読み取られているため、未定義の動作が呼び出されます。

修正は単純明快pc++で、printfコールから移動することです。

于 2013-09-22T01:33:00.663 に答える