compiler optimizes out pointer to function when address is assigned manually

Viewed 369

I need to call a function at address 0xDD2:

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

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

This code works:

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

However, this one doesn't:

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

The compiler (XC8) says: main.c:5:: warning: (759) expression generates no code and debugger passes these lines while debugging.

I need second one (call function just by its address). Why compiler optimizes it out? Is there any mistake in pointer assignment? Changing optimization level of compiler didn't help.

3 Answers

This seems to do what you asked for:

/*
 * File:   main.c
 * Author: dan1138
 * Target: PIC18F47Q10
 * Compiler: XC8 v2.20
 * 
 * See: https://stackoverflow.com/questions/62282036/compiler-optimizes-out-pointer-to-function-when-address-is-assigned-manually/62297967#62297967
 *
 * 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();
}    

Disassembly:

!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

Should really be a comment, but it contains code and reference from standard.

I have no XC8 compiler at hand so I tried to mimic something with CLang. This compiles with no warning and correctly display 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;
}

I controlled in C11 reference manual (draft n1570) at 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....

My understanding is that converting from a plain integer to a function pointer is legal but implementation defined. What is the compatibility level of your compiler, and are there any flags that could control that behaviour?

Based on what I read in XC8 manual and some discussions at another forum, it's not a good idea to put objects at a fixed address to use them in another project because of compiled stack and some other reasons.
However, if you just need to prevent compiler from optimizing out pointer to function when address is assigned manually, Use code in this comment or this answer.

Related