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