2

次のコードは、フラグ Od、O1 では期待どおりに動作しますが、O2、Ox では失敗します。理由はありますか?

編集:「失敗」とは、関数が何もせず、ただ戻っているように見えることを意味します。

void thread_sleep()
{
    listIterator nextThread = getNextThread();
    void * pStack = 0;
    struct ProcessControlBlock * currPcb = pPCBs->getData(currentThread);
    struct ProcessControlBlock * nextPcb = pPCBs->getData(nextThread);

    if(currentThread == nextThread)
    {
        return;
    }
    else
    {
        currentThread = nextThread;
        __asm pushad            // push general purpose registers
        __asm pushfd            // push control registers
        __asm mov pStack, esp   // store stack pointer in temporary

        currPcb->pStack = pStack;   // store current stack pointer in pcb
        pStack = nextPcb->pStack;   // grab new stack pointer from pcb

        if(nextPcb->state == RUNNING_STATE)// only pop if function was running before
        {
            __asm mov esp, pStack       // restore new stack pointer
            __asm popfd
            __asm popad;
        }
        else
        {
            __asm mov esp, pStack       // restore new stack pointer
            startThread(currentThread);
        }
    }
}

// 提案を実装した後: (まだ機能しません)

listIterator nextThread = getNextThread();
struct ProcessControlBlock * currPcb = pPCBs->getData(currentThread);
struct ProcessControlBlock * nextPcb = pPCBs->getData(nextThread);
void * pStack = 0;
void * pNewStack = nextPcb->pStack; // grab new stack pointer from pcb
pgVoid2 = nextPcb->pStack;

if(currentThread == nextThread)
{
    return;
}
else
{
    lastThread = currentThread; // global var
    currentThread = nextThread;


    if(nextPcb->state == RUNNING_STATE)// only pop if function was running before
    {
        __asm pushad                // push general purpose registers
        __asm pushfd                // push control registers
        __asm mov pgVoid1, esp      // store stack pointer in temporary
        __asm mov esp, pgVoid2      // restore new stack pointer
        __asm popfd
        __asm popad;

        {
            struct ProcessControlBlock * pcb = pPCBs->getData(lastThread);
            pcb->pStack = pgVoid1; // store old stack pointer in pcb
        }
    }
    else
    {
        __asm pushad                // push general purpose registers
        __asm pushfd                // push control registers
        __asm mov pgVoid1, esp  // store stack pointer in temporary
        __asm mov esp, pgVoid2      // restore new stack pointer

        {
            struct ProcessControlBlock * pcb = pPCBs->getData(lastThread);
            pcb->pStack = pgVoid1; // store old stack pointer in pcb
        }
        startThread(currentThread);
    }
}
4

2 に答える 2

3

これは、コンパイラーがより高い最適化レベルで特定のフレーム ポインター レジスターを使用していないために、追加の汎用レジスターが解放された可能性があります。

これは、コンパイラがpStackスタック ポインターからのオフセットを使用してローカル変数にアクセスすることを意味します。pushadによってスタック ポインターが調整された後は、これを正しく行うことができません。またpushfd、スタック ポインターが変更されることを想定していません。

これを回避するにはasm、スタック ポインターが正しく復元されるまで、これらのステートメントの後に C コードを配置しないでください。最初pushadからpopadorまでのすべてstartThread()をアセンブラーにする必要があります。このようにして、ローカル変数のアドレスをロードし、アクセスが正しく行われるようにすることができます。

于 2011-11-11T12:12:12.273 に答える
2

インラインアセンブラを使用する場合、さまざまな-Oxオプションを使用してコンパイルしたときに、コードが実際にどのように変更されたか(または変更されたかどうか)を確認する必要があります。バイナリでこれを試してください:

objdump -s your_program

それはコードのヒープを提供しますが、対応するコードセクションを見つけることはそれほど難しいことではありません(アセンブリまたは関数名を検索してください)。

ちなみに、インラインアセンブリでは大規模な最適化はうまくいかないことを教えられたので、このためにアセンブラルーチンを.Sファイルに分離する傾向があります。

于 2011-11-12T22:26:11.033 に答える