Флаги компилятора изменяют поведение кода (O2, Ox)

#c #visual-studio-2010 #compiler-optimization #compiler-flags

#c #visual-studio-2010 #оптимизация компилятора #флаги компилятора

Вопрос:

Следующий код работает, как и ожидалось, с флагами 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);
    }
}
  

Комментарии:

1. Что именно вы подразумеваете под «сбоем»?

2. Может быть какая-то невидимая ошибка, которая появляется при применении другой оптимизации, и я встречал такую ошибку раньше.

3. Это работает не так, как ожидалось, функция должна запланировать другой «поток» при вызове. Под «сбой» я подразумеваю, что это так, как если бы функция вообще не выполнялась и возвращалась мгновенно

4. Вы пробовали пошаговое выполнение в отладчике?

5. Вся функция оптимизирована, так что нет ничего, через что нужно проходить. Когда оптимизация отключена, она работает так, как ожидалось

Ответ №1:

Вероятно, это связано с тем, что ваш компилятор не использует определенный регистр указателя фрейма на более высоких уровнях оптимизации, что освобождает дополнительный регистр общего назначения.

Это означает, что компилятор обращается к локальной переменной pStack , используя смещение от указателя стека. Он не может сделать это правильно после того, как указатель стека был скорректирован pushad и pushfd — он не ожидает изменения указателя стека.

Чтобы обойти это, вы не должны помещать какой-либо код C после этих asm инструкций, пока указатель стека не будет правильно восстановлен: все, начиная с первого pushad и заканчивая popad or startThread() , должно быть на ассемблере. Таким образом, вы можете загрузить адрес локальных переменных и убедиться, что обращения выполняются правильно.

Комментарии:

1. Я пытался заставить всю сборку выполняться в одном блоке, это не помогает ситуации.

Ответ №2:

Поскольку вы используете встроенный ассемблер, вы, вероятно, захотите посмотреть, как на самом деле изменяется (или есть ли) код при компиляции с различными параметрами -Ox. Попробуйте это на своем двоичном:

 objdump -s your_program
  

Это выдает кучу кода, но найти соответствующий раздел кода не должно быть так сложно (поиск по вашей сборке или по именам функций).

Кстати, меня учили, что интенсивная оптимизация не очень хорошо работает со встроенной сборкой, поэтому я склонен разделять процедуры ассемблера на файлы .S из-за этого.