编译器标志更改代码行为(O2、Ox)

Posted

技术标签:

【中文标题】编译器标志更改代码行为(O2、Ox)【英文标题】:Compiler flags change code behavior (O2, Ox) 【发布时间】:2011-11-11 11:53:48 【问题描述】:

以下代码使用标志 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】:

这可能是因为您的编译器没有在更高的优化级别上使用特定的帧指针寄存器,这释放了一个额外的通用寄存器。

这意味着编译器使用堆栈指针的偏移量访问局部变量pStack。在堆栈指针被pushadpushfd 调整后,它无法正确执行此操作 - 它不期望堆栈指针发生更改。

为了解决这个问题,您不应该在那些 asm 语句之后放置任何 C 代码,直到堆栈指针被正确恢复:从第一个 pushadpopadstartThread() 的所有内容都应该是在汇编程序中。这样,您可以加载局部变量的地址并确保访问正确完成。

【讨论】:

我试图让所有程序集在一个块中执行,但对这种情况没有帮助。【参考方案2】:

当您使用内联汇编程序时,您可能想看看在使用各种 -Ox 选项编译代码时,代码是如何(或是否)真正修改的。在你的二进制文件上试试这个:

objdump -s your_program

它提供了一堆代码,但找到相应的代码部分应该不难(搜索您的程序集或函数名称)。

顺便说一句,我被告知,内联汇编不能很好地进行大量优化,因此我倾向于将汇编程序例程分离到 .S 文件中。

【讨论】:

以上是关于编译器标志更改代码行为(O2、Ox)的主要内容,如果未能解决你的问题,请参考以下文章

Linux 上 fortran 代码的 Intel Vtune 奇怪行为

何时为 gcc 使用 -O2 标志?

指定 O2 标志时 gcc 链接到错误的 GLIBCXX 版本

哪个 gcc O2 标志可能导致 fp 计算失败?

GCC - 包含编译标志的宏

从可执行文件中找出编译优化标志