#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 из-за этого.