手动分配地址时,编译器优化指向函数的指针

compiler optimizes out pointer to function when address is assigned manually

我需要在地址 0xDD2 调用一个函数:

// foo.h
void foo(void) __at(0xDD2);

// foo.c
#include "foo.h"
void foo(void)
{
    // some code
}

此代码有效:

#include "foo.h"
void main(void)
{
    void (*a)(void) = &foo;
    a();
}

然而,这个没有:

#include "foo.h"
void main(void)
{
    void (*a)(void) = (void (*)(void))(0x0DD2);
    a();
}

编译器 (XC8) 说:main.c:5:: warning: (759) expression generates no code 调试器在调试时传递这些行。

我需要第二个(仅通过地址调用函数)。为什么编译器会优化它?指针赋值有没有错误?更改编译器的优化级别没有帮助。

确实应该是评论,但它包含来自标准的代码和参考。

我手边没有 XC8 编译器,所以我尝试用 CLang 模仿一些东西。这编译没有警告并正确显示 In foo:

#include <stdio.h>
#include <stdint.h>

void foo(void) {
    printf("In foo\n");
}

int main() {
    intptr_t f = (intptr_t) &foo;
    printf("%lx\n", f);
    void (*a)(void) = (void (*)(void))f;
    (*a)();        // Same behaviour with a();
    return 0;
}

我在 C11 参考手册(草案 n1570)中控制了 6.3.2.3 Language/Conversions/Other operands/Pointers §5-6:

5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined...

6 Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined....

我的理解是,从普通整数到函数指针的转换是合法的,但实现定义了。您的编译器的兼容性级别是多少,是否有任何可以控制该行为的标志?

这似乎符合您的要求:

/*
 * File:   main.c
 * Author: dan1138
 * Target: PIC18F47Q10
 * Compiler: XC8 v2.20
 * 
 * See: 
 *
 * Created on June 9, 2020, 11:42 PM
 */
#pragma config FEXTOSC = OFF, RSTOSC = HFINTOSC_64MHZ, CLKOUTEN = OFF
#pragma config CSWEN = ON, FCMEN = OFF, MCLRE = EXTMCLR, PWRTE = OFF
#pragma config LPBOREN = OFF, BOREN = SBORDIS, BORV = VBOR_190
#pragma config ZCD = OFF, PPS1WAY = OFF, STVREN = ON, XINST = OFF
#pragma config WDTCPS = WDTCPS_31, WDTE = OFF, WDTCWS = WDTCWS_7, WDTCCS = SC
#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF
#pragma config WRT4 = OFF, WRT5 = OFF, WRT6 = OFF, WRT7 = OFF
#pragma config WRTC = OFF, WRTB = OFF, WRTD = OFF
#pragma config SCANE = ON, LVP = OFF, CP = OFF, CPD = OFF
#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF
#pragma config EBTR4 = OFF, EBTR5 = OFF, EBTR6 = OFF, EBTR7 = OFF
#pragma config EBTRB = OFF

#include <xc.h>

void main(void)
{
    extern void foo(void) __at(0xDD2);
    void (* volatile a)(void) = foo;

    a();
}    

反汇编:

!void main(void)
!{
!    extern void foo(void) __at(0xDD2);
!    void (* volatile a)(void) = foo;
0x1FFD4: MOVLW 0xD2
0x1FFD6: MOVWF __pcstackCOMRAM, ACCESS
0x1FFD8: MOVLW 0xD
0x1FFDA: MOVWF 0x2, ACCESS
0x1FFDC: MOVLW 0x0
0x1FFDE: MOVWF 0x3, ACCESS
!    a();
0x1FFE0: RCALL 0xFFE6
0x1FFE2: GOTO 0x1FFFA
0x1FFE4: NOP
0x1FFE6: PUSH
0x1FFE8: MOVWF PCLATH, ACCESS
0x1FFEA: MOVF __pcstackCOMRAM, W, ACCESS
0x1FFEC: MOVWF TOS, ACCESS
0x1FFEE: MOVF 0x2, W, ACCESS
0x1FFF0: MOVWF TOSH, ACCESS
0x1FFF2: MOVF 0x3, W, ACCESS
0x1FFF4: MOVWF TOSU, ACCESS
0x1FFF6: MOVF PCLATH, W, ACCESS
0x1FFF8: RETURN 0
!}
0x1FFFA: BSF _ccovbit2_1, 0, ACCESS
0x1FFFC: GOTO 0x0
0x1FFFE: NOP

根据我在 XC8 手册和 some discussions 在另一个论坛上阅读的内容,由于编译堆栈和其他一些原因,将对象放在固定地址以在另一个项目中使用它们并不是一个好主意。
但是,如果您只需要防止编译器在手动分配地址时优化指向函数的指针,请使用 or .

中的代码