Skip to content

Commit 9278443

Browse files
committed
Add little-endian PowerPC (ppc64le) support to VM JIT compiler
The PowerPC JIT (vm_powerpc.c) was written for big-endian ppc/ppc64 only. This adds support for little-endian ppc64le (ELFv2 ABI) with the following changes: Endianness fixes: - Add SE/HI16/LO16/FPRHI/FPRLO macros for endian-independent access to union half-words and FPR double-word halves in memory - Replace big-endian byte-reversal trick for immediate decoding with portable shift-based approach (matching vm_x86.c) - Fix all union short[2] accesses and FPR store/load offsets for OP_CVIF and OP_CVFI ELFv2 ABI support: - Add ELFV1/ELFV2 detection macros using _CALL_ELF - Make OPD (Official Procedure Descriptor) conditional on ELFv1 only; ELFv2 uses direct code addresses for function pointers - Set r12 = target address before indirect calls (bctrl) as required by the ELFv2 global entry point convention - Fix entry point selection to use direct code address on ELFv2 Bug fixes (affect all PowerPC platforms): - Remove incorrect Z_Free of Hunk_Alloc'd instructionPointers, which caused 'Z_Free: freed a pointer without ZONEID' crash - Add __builtin___clear_cache() before mprotect() to synchronize split D/I caches after writing generated code Tested on ppc64le (Fedora 43, GCC 15.2.1) with full retail Quake III Arena assets: FFA, CTF, and Team Arena modes with up to 8 bots across multiple maps including 5-minute stress tests.
1 parent 30912dd commit 9278443

1 file changed

Lines changed: 73 additions & 37 deletions

File tree

code/qcommon/vm_powerpc.c

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ typedef enum powerpc_iname {
151151
iBLTm, iBC, iBCL, iB, iBL, iBLR, iBCTR, iBCTRL, iRLWINM, iNOP, iORI,
152152
iXORIS, iLDX, iLWZX, iSLW, iAND, iSUB, iLBZX, iNEG, iNOT, iSTWX, iSTBX,
153153
iMULLW, iADD, iLHZX, iXOR, iMFLR, iSTHX, iMR, iOR, iDIVWU, iMTLR,
154-
iMTCTR, iDIVW, iLFSX, iSRW, iSTFSX, iSRAW, iEXTSH, iEXTSB, iLWZ, iLBZ,
154+
iMTCTR, iDIVW, iLFSX, iSRW, iSTFSX, iSRAW, iEXTSH, iEXTSB, iEXTSW, iLWZ, iLBZ,
155155
iSTW, iSTWU, iSTB, iLHZ, iSTH, iLFS, iLFD, iSTFS, iSTFD, iLD, iFDIVS,
156156
iFSUBS, iFADDS, iFMULS, iSTD, iSTDU, iFRSP, iFCTIWZ, iFSUB, iFNEG,
157157
} powerpc_iname_t;
@@ -1100,6 +1100,7 @@ static const struct powerpc_opcode powerpc_opcodes[] = {
11001100
{ "sraw", XRC(31,792,0), X_MASK, PPCCOM, { RA, RS, RB } },
11011101
{ "extsh", XRC(31,922,0), XRB_MASK, PPCCOM, { RA, RS } },
11021102
{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} },
1103+
{ "extsw", XRC(31,986,0), XRB_MASK, PPC64, { RA, RS } },
11031104

11041105
{ "lwz", OP(32), OP_MASK, PPCCOM, { RT, D, RA0 } },
11051106
{ "lbz", OP(34), OP_MASK, COM, { RT, D, RA0 } },
@@ -1221,11 +1222,38 @@ PPC_Malloc( size_t size )
12211222
# define SA( a, b ) (b)
12221223
#endif
12231224

1225+
/* Select Endian - first for big endian, second for little endian */
1226+
#ifdef __BIG_ENDIAN__
1227+
# define SE( a, b ) (a)
1228+
#else
1229+
# define SE( a, b ) (b)
1230+
#endif
1231+
1232+
/* indices for accessing high/low 16-bit halves of a 32-bit int via
1233+
* a union of short[2]; on big-endian [0] is high, on little-endian [1] is */
1234+
#define HI16 SE( 0, 1 )
1235+
#define LO16 SE( 1, 0 )
1236+
1237+
/* offset of high/low 32-bit word within a 64-bit double stored in memory */
1238+
#define FPRHI SE( 0, 4 )
1239+
#define FPRLO SE( 4, 0 )
1240+
12241241
#define ELF32 SL( SA( 1, 0 ), 0 )
12251242
#define ELF64 SL( 0, SA( 1, 0 ) )
12261243
#define OSX32 SL( SA( 0, 1 ), 0 )
12271244
#define OSX64 SL( 0, SA( 0, 1 ) )
12281245

1246+
/* Distinguish ELFv1 (ppc64be) from ELFv2 (ppc64le) ABI.
1247+
* ELFv1 uses Official Procedure Descriptors (OPD) for function pointers;
1248+
* ELFv2 uses direct code addresses. */
1249+
#if ELF64 && defined(_CALL_ELF) && _CALL_ELF == 2
1250+
# define ELFV2 1
1251+
# define ELFV1 0
1252+
#else
1253+
# define ELFV2 0
1254+
# define ELFV1 ELF64
1255+
#endif
1256+
12291257
/* native length load/store instructions ( L stands for long ) */
12301258
#define iSTLU SL( iSTWU, iSTDU )
12311259
#define iSTL SL( iSTW, iSTD )
@@ -1248,11 +1276,11 @@ PPC_Malloc( size_t size )
12481276
* prepared properly */
12491277
#define STACK_RTEMP (-16)
12501278

1251-
#if ELF64
1279+
#if ELFV1
12521280
/*
1253-
* Official Procedure Descriptor
1281+
* Official Procedure Descriptor (ELFv1 only)
12541282
* we need to prepare one for generated code if we want to call it
1255-
* as function
1283+
* as function. ELFv2 (ppc64le) uses direct code addresses instead.
12561284
*/
12571285
typedef struct {
12581286
void *function;
@@ -1411,8 +1439,8 @@ typedef struct VM_Data {
14111439
// fixed number used to convert from integer to float
14121440
unsigned int floatBase; // 0x59800004
14131441

1414-
#if ELF64
1415-
// official procedure descriptor
1442+
#if ELFV1
1443+
// official procedure descriptor (ELFv1 only)
14161444
opd_t opd;
14171445
#endif
14181446

@@ -1873,9 +1901,9 @@ PPC_EmitConst( source_instruction_t * const i_const )
18731901
in( iLI, rFIRST, 0 );
18741902
in( iORI, rFIRST, rFIRST, i_const->arg.i );
18751903
} else {
1876-
in( iLIS, rFIRST, i_const->arg.ss[ 0 ] );
1877-
if ( i_const->arg.us[ 1 ] != 0 )
1878-
in( iORI, rFIRST, rFIRST, i_const->arg.us[ 1 ] );
1904+
in( iLIS, rFIRST, i_const->arg.ss[ HI16 ] );
1905+
if ( i_const->arg.us[ LO16 ] != 0 )
1906+
in( iORI, rFIRST, rFIRST, i_const->arg.us[ LO16 ] );
18791907
}
18801908

18811909
} else {
@@ -2177,9 +2205,9 @@ VM_CompileFunction( source_instruction_t * const i_first )
21772205
in( iLI, r3, 0 );
21782206
in( iORI, r3, r3, i_const->arg.i );
21792207
} else {
2180-
in( iLIS, r3, i_const->arg.ss[ 0 ] );
2181-
if ( i_const->arg.us[ 1 ] != 0 )
2182-
in( iORI, r3, r3, i_const->arg.us[ 1 ] );
2208+
in( iLIS, r3, i_const->arg.ss[ HI16 ] );
2209+
if ( i_const->arg.us[ LO16 ] != 0 )
2210+
in( iORI, r3, r3, i_const->arg.us[ LO16 ] );
21832211
}
21842212
gpr_pos--;
21852213
} else {
@@ -2240,6 +2268,9 @@ VM_CompileFunction( source_instruction_t * const i_first )
22402268
in( iLI, r3, i_const->arg.si ); // negative value
22412269
in( iMR, r4, rPSTACK ); // push PSTACK on argument list
22422270

2271+
#if ELFV2
2272+
in( iMR, r12, r0 ); // ELFv2: r12 must hold target addr
2273+
#endif
22432274
in( iMTCTR, r0 );
22442275
in( iBCTRL );
22452276
}
@@ -2257,16 +2288,20 @@ VM_CompileFunction( source_instruction_t * const i_first )
22572288
in( iRLWINM, rFIRST, rFIRST, GPRLEN_SHIFT, 0, 31-GPRLEN_SHIFT ); // mul * GPRLEN
22582289
in( iLLX, r0, rFIRST, r0 ); // load pointer
22592290

2260-
in( iB, +4*(3 + (rFIRST != r3 ? 1 : 0) ) ); // XXX jump !
2291+
in( iB, +4*(4 + (rFIRST != r3 ? 1 : 0) ) ); // skip syscall-specific code to common tail
22612292

22622293
/* syscall */
22632294
in( iLL, r0, VM_Data_Offset( AsmCall ), rVMDATA ); // get asmCall pointer
22642295
/* rFIRST can be r3 or some static register */
22652296
if ( rFIRST != r3 )
22662297
in( iMR, r3, rFIRST ); // push OPSTACK top value on argument list
2298+
in( iEXTSW, r3, r3 ); // sign-extend 32-bit syscall number for 64-bit ABI
22672299
in( iMR, r4, rPSTACK ); // push PSTACK on argument list
22682300

22692301
/* common code */
2302+
#if ELFV2
2303+
in( iMR, r12, r0 ); // ELFv2: r12 must hold target addr
2304+
#endif
22702305
in( iMTCTR, r0 );
22712306
in( iBCTRL );
22722307

@@ -2304,8 +2339,8 @@ VM_CompileFunction( source_instruction_t * const i_first )
23042339
MAYBE_EMIT_CONST();
23052340
{
23062341
signed long int hi, lo;
2307-
hi = i_now->arg.ss[ 0 ];
2308-
lo = i_now->arg.ss[ 1 ];
2342+
hi = i_now->arg.ss[ HI16 ];
2343+
lo = i_now->arg.ss[ LO16 ];
23092344
if ( lo < 0 )
23102345
hi += 1;
23112346

@@ -2622,15 +2657,18 @@ VM_CompileFunction( source_instruction_t * const i_first )
26222657
in( iLI, r5, 0 );
26232658
r5_ori = i_now->arg.i;
26242659
} else {
2625-
in( iLIS, r5, i_now->arg.ss[ 0 ] );
2626-
r5_ori = i_now->arg.us[ 1 ];
2660+
in( iLIS, r5, i_now->arg.ss[ HI16 ] );
2661+
r5_ori = i_now->arg.us[ LO16 ];
26272662
}
26282663

26292664
in( iLL, r0, VM_Data_Offset( BlockCopy ), rVMDATA ); // get blockCopy pointer
26302665

26312666
if ( r5_ori )
26322667
in( iORI, r5, r5, r5_ori );
26332668

2669+
#if ELFV2
2670+
in( iMR, r12, r0 ); // ELFv2: r12 must hold target addr
2671+
#endif
26342672
in( iMTCTR, r0 );
26352673

26362674
if ( rFIRST != r4 )
@@ -2664,8 +2702,8 @@ VM_CompileFunction( source_instruction_t * const i_first )
26642702
EMIT_FALSE_CONST();
26652703

26662704
signed short int hi, lo;
2667-
hi = i_const->arg.ss[ 0 ];
2668-
lo = i_const->arg.ss[ 1 ];
2705+
hi = i_const->arg.ss[ HI16 ];
2706+
lo = i_const->arg.ss[ LO16 ];
26692707
if ( lo < 0 )
26702708
hi += 1;
26712709

@@ -2800,8 +2838,8 @@ VM_CompileFunction( source_instruction_t * const i_first )
28002838
fpr_pos++;
28012839
in( iXORIS, rFIRST, rFIRST, 0x8000 );
28022840
in( iLIS, r0, 0x4330 );
2803-
in( iSTW, rFIRST, stack_temp + 4, r1 );
2804-
in( iSTW, r0, stack_temp, r1 );
2841+
in( iSTW, rFIRST, stack_temp + FPRLO, r1 );
2842+
in( iSTW, r0, stack_temp + FPRHI, r1 );
28052843
in( iLFS, fTMP, VM_Data_Offset( floatBase ), rVMDATA );
28062844
in( iLFD, fFIRST, stack_temp, r1 );
28072845
in( iFSUB, fFIRST, fFIRST, fTMP );
@@ -2814,7 +2852,7 @@ VM_CompileFunction( source_instruction_t * const i_first )
28142852
gpr_pos++;
28152853
in( iFCTIWZ, fFIRST, fFIRST );
28162854
in( iSTFD, fFIRST, stack_temp, r1 );
2817-
in( iLWZ, rFIRST, stack_temp + 4, r1 );
2855+
in( iLWZ, rFIRST, stack_temp + FPRLO, r1 );
28182856
fpr_pos--;
28192857
break;
28202858
}
@@ -2994,8 +3032,8 @@ PPC_ComputeCode( vm_t *vm )
29943032

29953033
vm_data_t *data = (vm_data_t *)dataAndCode;
29963034

2997-
#if ELF64
2998-
// prepare Official Procedure Descriptor for the generated code
3035+
#if ELFV1
3036+
// ELFv1: prepare Official Procedure Descriptor for the generated code
29993037
// and retrieve real function pointer for helper functions
30003038

30013039
opd_t *ac = (void *)VM_AsmCall, *bc = (void *)VM_BlockCopy;
@@ -3084,12 +3122,8 @@ VM_Compile( vm_t *vm, vmHeader_t *header )
30843122
i_first = PPC_Malloc( sizeof( source_instruction_t ) );
30853123
i_first->next = NULL;
30863124

3087-
// realloc instructionPointers with correct size
3088-
// use Z_Malloc so vm.c will be able to free the memory
3089-
if ( sizeof( void * ) != sizeof( int ) ) {
3090-
Z_Free( vm->instructionPointers );
3091-
vm->instructionPointers = Z_Malloc( header->instructionCount * sizeof( void * ) );
3092-
}
3125+
// vm->instructionPointers is already allocated as intptr_t* by vm.c
3126+
// (pointer-sized entries), so no reallocation needed
30933127
di_pointers = (void *)vm->instructionPointers;
30943128
memset( di_pointers, 0, header->instructionCount * sizeof( void * ) );
30953129

@@ -3123,12 +3157,10 @@ VM_Compile( vm_t *vm, vmHeader_t *header )
31233157
i_now->next = NULL;
31243158

31253159
if ( vm_opInfo[op] & opImm4 ) {
3126-
union {
3127-
unsigned char b[4];
3128-
unsigned int i;
3129-
} c = { { code[ pc + 3 ], code[ pc + 2 ], code[ pc + 1 ], code[ pc + 0 ] }, };
3130-
3131-
i_now->arg.i = c.i;
3160+
i_now->arg.i = (unsigned char)code[ pc ]
3161+
| ( (unsigned char)code[ pc + 1 ] << 8 )
3162+
| ( (unsigned char)code[ pc + 2 ] << 16 )
3163+
| ( (unsigned char)code[ pc + 3 ] << 24 );
31323164
pc += 4;
31333165
} else if ( vm_opInfo[op] & opImm1 ) {
31343166
i_now->arg.b = code[ pc++ ];
@@ -3152,6 +3184,10 @@ VM_Compile( vm_t *vm, vmHeader_t *header )
31523184
Com_Printf( S_COLOR_RED "Pointer %ld not initialized !\n", i );
31533185
#endif
31543186

3187+
/* flush data cache and invalidate instruction cache for generated code;
3188+
* PowerPC has split D/I caches and requires explicit synchronization */
3189+
__builtin___clear_cache( vm->codeBase, vm->codeBase + vm->codeLength );
3190+
31553191
/* mark memory as executable and not writeable */
31563192
if ( mprotect( vm->codeBase, vm->codeLength, PROT_READ|PROT_EXEC ) ) {
31573193

@@ -3209,7 +3245,7 @@ VM_CallCompiled( vm_t *vm, int *args )
32093245
/* call generated code */
32103246
{
32113247
int ( *entry )( void *, int, void * );
3212-
#ifdef __PPC64__
3248+
#if ELFV1
32133249
entry = (void *)&(vm_dataAndCode->opd);
32143250
#else
32153251
entry = (void *)(vm->codeBase + vm_dataAndCode->dataLength);

0 commit comments

Comments
 (0)