【问题标题】:How do you control what your C compiler Optimizes?您如何控制 C 编译器优化的内容?
【发布时间】:2010-04-15 19:47:24
【问题描述】:

我正在使用 Silicon Labs IDE 和 SDCC 编译器用 C 语言为嵌入式设备编写固件。器件架构基于 8051 系列。有问题的函数如下所示。该功能用于设置我的 MCU 上的端口以驱动步进电机。它被中断处理程序调用。大开关语句只是将端口设置为下一个电机步进的正确值。该函数的底部查看来自霍尔效应传感器的输入和移动的步数,以检测电机是否已停止。问题是,由于某种原因,看起来像 if (StallDetector > (GapSize + 20)) { HandleStallEvent(); } 的第二个 IF 语句似乎总是被优化出来。如果我尝试在HandleStallEvent() 调用处放置断点,IDE 会给我一条消息,说“与此行号没有地址相关性”。我在阅读汇编方面还不够好,无法知道它在做什么,但我从下面的 asm 输出中粘贴了一个 sn-p。任何帮助将不胜感激。

void OperateStepper(void)
{
    //static bit LastHomeMagState = HomeSensor;
    static bit LastPosMagState = PosSensor;
    if(PulseMotor)
    {
        if(MoveDirection == 1) // Go clockwise
        {
            switch(STEPPER_POSITION) 
            {
                case 'A': 
                     STEPPER_POSITION = 'B';
                     P1 = 0xFD;
                     break;
                case 'B':
                     STEPPER_POSITION = 'C';
                     P1 = 0xFF;
                     break;
                case 'C':
                     STEPPER_POSITION = 'D';
                     P1 = 0xFE;
                     break;
                case 'D':
                     STEPPER_POSITION = 'A';
                     P1 = 0xFC;
                     break; 
                default:
                     STEPPER_POSITION = 'A';
                     P1 = 0xFC;
            }   //end switch
        }
        else                // Go CounterClockwise
        {
            switch(STEPPER_POSITION) 
            {
                case 'A': 
                     STEPPER_POSITION = 'D';
                     P1 = 0xFE;
                     break;
                case 'B': 
                     STEPPER_POSITION = 'A';
                     P1 = 0xFC;
                     break;
                case 'C': 
                     STEPPER_POSITION = 'B';
                     P1 = 0xFD;
                     break;
                case 'D': 
                     STEPPER_POSITION = 'C';
                     P1 = 0xFF;
                     break; 
                default: 
                     STEPPER_POSITION = 'A';
                     P1 = 0xFE;
            }   //end switch
        }   //end else

        MotorSteps++;
        StallDetector++;

        if(PosSensor != LastPosMagState)
        {
            StallDetector = 0;

            LastPosMagState = PosSensor;
        }
        else
        {
            if (PosSensor == ON) 
            {
                if (StallDetector > (MagnetSize + 20))
                {
                    HandleStallEvent();
                }
            }
            else if (PosSensor == OFF) 
            {
                if (StallDetector > (GapSize + 20))
                {
                    HandleStallEvent();
                }
            }
        }

    }   //end if PulseMotor
}

...以及该函数底部的asm输出...

;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:653: if(PosSensor != LastPosMagState)
    mov c,_P1_4
    jb  _OperateStepper_LastPosMagState_1_1,00158$
    cpl c
00158$:
    jc  00126$
    C$MotionControl.c$655$3$7 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:655: StallDetector = 0;
    clr a
    mov _StallDetector,a
    mov (_StallDetector + 1),a
    C$MotionControl.c$657$3$7 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:657: LastPosMagState = PosSensor;
    mov c,_P1_4
    mov _OperateStepper_LastPosMagState_1_1,c
    ret
00126$:
    C$MotionControl.c$661$2$8 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:661: if (PosSensor == ON) 
    jb  _P1_4,00123$
    C$MotionControl.c$663$4$9 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:663: if (StallDetector > (MagnetSize + 20))
    mov a,_MagnetSize
    mov r2,a
    rlc a
    subb    a,acc
    mov r3,a
    mov a,#0x14
    add a,r2
    mov r2,a
    clr a
    addc    a,r3
    mov r3,a
    clr c
    mov a,r2
    subb    a,_StallDetector
    mov a,r3
    subb    a,(_StallDetector + 1)
    jnc 00130$
    C$MotionControl.c$665$5$10 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:665: HandleStallEvent();
    ljmp    _HandleStallEvent
00123$:
    C$MotionControl.c$668$2$8 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:668: else if (PosSensor == OFF) 
    jnb _P1_4,00130$
    C$MotionControl.c$670$4$11 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:670: if (StallDetector > (GapSize + 20))
    mov a,#0x14
    add a,_GapSize
    mov r2,a
    clr a
    addc    a,(_GapSize + 1)
    mov r3,a
    clr c
    mov a,r2
    subb    a,_StallDetector
    mov a,r3
    subb    a,(_StallDetector + 1)
    jnc 00130$
    C$MotionControl.c$672$5$12 ==.
;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:672: HandleStallEvent();
    C$MotionControl.c$678$2$1 ==.
    XG$OperateStepper$0$0 ==.
    ljmp    _HandleStallEvent
00130$:
    ret

在我看来,编译器没有从 asm 的外观优化第二个 if 语句,但如果是这种情况,为什么 IDE 不允许我在此处设置断点?也许这只是一个愚蠢的 IDE!

【问题讨论】:

    标签: c optimization compiler-construction function sdcc


    【解决方案1】:

    称为“尾调用优化”。

    OperateStepper() 在调用 HandleStallEvent() 之后什么都不做,所以返回它没有意义。您只是对 RET 执行 RET,这是对指令 AND 堆栈槽的浪费。

    阅读“Lambda: The Ultimate ...”麻省理工学院人工智能实验室备忘录了解更多详情。

    在 HandleStallEvent() 例程上设置断点,而不是调用。

    【讨论】:

    • ++ 我应该看到的。感谢您的关注。
    【解决方案2】:

    IF 语句没有被优化。这是它的代码。

        C$MotionControl.c$670$4$11 ==.
    ;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:670: if (StallDetector > (GapSize + 20))
        mov a,#0x14       ; r2,r3 = 20 + GapSize
        add a,_GapSize    ; (adding a 16-bit number in two 8-bit steps)
        mov r2,a
        clr a
        addc    a,(_GapSize + 1)
        mov r3,a
        clr c
        mov a,r2          ; subtracting in two 8-bit steps
        subb    a,_StallDetector
        mov a,r3
        subb    a,(_StallDetector + 1)
        jnc 00130$        ; jump if carry not set (fall through if carry set)
        C$MotionControl.c$672$5$12 ==.
    ;   C:\SiLabs\Optec Programs\HSFW_HID_SDCC_2\MotionControl.c:672: HandleStallEvent();
        C$MotionControl.c$678$2$1 ==.
        XG$OperateStepper$0$0 ==.
        ljmp    _HandleStallEvent    ; it knows HandleStallEvent does not return!
    00130$:                            ; or rather, it knows this handler cannot return, so there's no need to call.
        ret
    

    我注意到在倒数第三行中,发生了一些有趣的事情。它没有对HandleStallEvent 进行函数调用。它在跳远,所以它显然知道HandleStallEvent 不能返回。我还看到,在上面的两行中,它定义了将行号与跳转指令相关联的汇编符号。所以,第 678 行有一个符号。如果 IDE 不允许您在第 678 行设置断点,也许您可​​以获取第 678 行的十六进制地址,并将其设置为十六进制地址。您可能会尝试的另一件事是在该行之前插入一个局部变量定义,如 int breakhere = 1,看看这是否会给您一些可以中断的指令。

    顺便说一句,你可以看到 CPU 是按照 8 位数字来思考的,所以如果你可以使用 char 而不是 short,它将节省指令。节省的时间是否值得取决于机器在这段代码中的时间百分比。

    顺便说一句,如果你想从这只小狗身上榨取性能,我在做嵌入式工作时所依赖的是随机停止 IDE(或英特尔“蓝盒”ICE)。 Here's something about that.

    【讨论】:

    • 迈克,我在下面回答了这个问题,但我也会发表评论。这称为“尾调用优化”。编译器看到在调用 HandleStallEvent() 之后什么都没有做,所以它知道让 HandleStallEvent() 返回到 OperateStepper() 不会完成任何有用的工作。
    • @John R. Strohm:呵呵。我应该看到的。
    【解决方案3】:

    您通常可以使用#pragma 语句调整优化器。 我不知道您的编译器的确切语法,但您应该能够在您的编译器/ide 随附的文档中找到它。

    类似的东西

    #pragma optimize( "", off )
    //now the function which should not be optimized
    #pragma optimize( "", on )
    

    【讨论】:

      【解决方案4】:

      如何配置优化取决于编译器。 SDCC 的manual 在第 3.28 节中列出了其优化选项。您可以在源代码级别使用命令行选项或编译指示。尝试全局禁用优化,看看是否得到相同的效果。通常在禁用优化的调试器中单步执行代码将消除无法设置断点的问题。如果这工作正常,您可以尝试使用编译指示在函数级别禁用可疑优化,以查看可能导致问题的优化。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-28
        • 2016-06-30
        • 2014-02-21
        • 1970-01-01
        • 1970-01-01
        • 2012-02-09
        • 1970-01-01
        相关资源
        最近更新 更多