@@ -104,6 +104,8 @@ class AArch64AsmPrinter : public AsmPrinter {
104
104
105
105
void LowerJumpTableDest (MCStreamer &OutStreamer, const MachineInstr &MI);
106
106
107
+ void LowerHardenedBRJumpTable (const MachineInstr &MI);
108
+
107
109
void LowerMOPS (MCStreamer &OutStreamer, const MachineInstr &MI);
108
110
109
111
void LowerSTACKMAP (MCStreamer &OutStreamer, StackMaps &SM,
@@ -1310,6 +1312,141 @@ void AArch64AsmPrinter::LowerJumpTableDest(llvm::MCStreamer &OutStreamer,
1310
1312
.addImm (Size == 4 ? 0 : 2 ));
1311
1313
}
1312
1314
1315
+ void AArch64AsmPrinter::LowerHardenedBRJumpTable (const MachineInstr &MI) {
1316
+ unsigned InstsEmitted = 0 ;
1317
+
1318
+ const MachineJumpTableInfo *MJTI = MF->getJumpTableInfo ();
1319
+ assert (MJTI && " Can't lower jump-table dispatch without JTI" );
1320
+
1321
+ const std::vector<MachineJumpTableEntry> &JTs = MJTI->getJumpTables ();
1322
+ assert (!JTs.empty () && " Invalid JT index for jump-table dispatch" );
1323
+
1324
+ // Emit:
1325
+ // mov x17, #<size of table> ; depending on table size, with MOVKs
1326
+ // cmp x16, x17 ; or #imm if table size fits in 12-bit
1327
+ // csel x16, x16, xzr, ls ; check for index overflow
1328
+ //
1329
+ // adrp x17, Ltable@PAGE ; materialize table address
1330
+ // add x17, Ltable@PAGEOFF
1331
+ // ldrsw x16, [x17, x16, lsl #2] ; load table entry
1332
+ //
1333
+ // Lanchor:
1334
+ // adr x17, Lanchor ; compute target address
1335
+ // add x16, x17, x16
1336
+ // br x16 ; branch to target
1337
+
1338
+ MachineOperand JTOp = MI.getOperand (0 );
1339
+
1340
+ unsigned JTI = JTOp.getIndex ();
1341
+ assert (!AArch64FI->getJumpTableEntryPCRelSymbol (JTI) &&
1342
+ " unsupported compressed jump table" );
1343
+
1344
+ const uint64_t NumTableEntries = JTs[JTI].MBBs .size ();
1345
+
1346
+ // cmp only supports a 12-bit immediate. If we need more, materialize the
1347
+ // immediate, using x17 as a scratch register.
1348
+ uint64_t MaxTableEntry = NumTableEntries - 1 ;
1349
+ if (isUInt<12 >(MaxTableEntry)) {
1350
+ EmitToStreamer (*OutStreamer, MCInstBuilder (AArch64::SUBSXri)
1351
+ .addReg (AArch64::XZR)
1352
+ .addReg (AArch64::X16)
1353
+ .addImm (MaxTableEntry)
1354
+ .addImm (0 ));
1355
+ ++InstsEmitted;
1356
+ } else {
1357
+ EmitToStreamer (*OutStreamer,
1358
+ MCInstBuilder (AArch64::MOVZXi)
1359
+ .addReg (AArch64::X17)
1360
+ .addImm (static_cast <uint16_t >(MaxTableEntry))
1361
+ .addImm (0 ));
1362
+ ++InstsEmitted;
1363
+ // It's sad that we have to manually materialize instructions, but we can't
1364
+ // trivially reuse the main pseudo expansion logic.
1365
+ // A MOVK sequence is easy enough to generate and handles the general case.
1366
+ for (int Offset = 16 ; Offset < 64 ; Offset += 16 ) {
1367
+ if ((MaxTableEntry >> Offset) == 0 )
1368
+ break ;
1369
+ EmitToStreamer (*OutStreamer,
1370
+ MCInstBuilder (AArch64::MOVKXi)
1371
+ .addReg (AArch64::X17)
1372
+ .addReg (AArch64::X17)
1373
+ .addImm (static_cast <uint16_t >(MaxTableEntry >> Offset))
1374
+ .addImm (Offset));
1375
+ ++InstsEmitted;
1376
+ }
1377
+ EmitToStreamer (*OutStreamer, MCInstBuilder (AArch64::SUBSXrs)
1378
+ .addReg (AArch64::XZR)
1379
+ .addReg (AArch64::X16)
1380
+ .addReg (AArch64::X17)
1381
+ .addImm (0 ));
1382
+ ++InstsEmitted;
1383
+ }
1384
+
1385
+ // This picks entry #0 on failure.
1386
+ // We might want to trap instead.
1387
+ EmitToStreamer (*OutStreamer, MCInstBuilder (AArch64::CSELXr)
1388
+ .addReg (AArch64::X16)
1389
+ .addReg (AArch64::X16)
1390
+ .addReg (AArch64::XZR)
1391
+ .addImm (AArch64CC::LS));
1392
+ ++InstsEmitted;
1393
+
1394
+ // Prepare the @PAGE/@PAGEOFF low/high operands.
1395
+ MachineOperand JTMOHi (JTOp), JTMOLo (JTOp);
1396
+ MCOperand JTMCHi, JTMCLo;
1397
+
1398
+ JTMOHi.setTargetFlags (AArch64II::MO_PAGE);
1399
+ JTMOLo.setTargetFlags (AArch64II::MO_PAGEOFF | AArch64II::MO_NC);
1400
+
1401
+ MCInstLowering.lowerOperand (JTMOHi, JTMCHi);
1402
+ MCInstLowering.lowerOperand (JTMOLo, JTMCLo);
1403
+
1404
+ EmitToStreamer (*OutStreamer, MCInstBuilder (AArch64::ADRP)
1405
+ .addReg (AArch64::X17)
1406
+ .addOperand (JTMCHi));
1407
+ ++InstsEmitted;
1408
+
1409
+ EmitToStreamer (*OutStreamer, MCInstBuilder (AArch64::ADDXri)
1410
+ .addReg (AArch64::X17)
1411
+ .addReg (AArch64::X17)
1412
+ .addOperand (JTMCLo)
1413
+ .addImm (0 ));
1414
+ ++InstsEmitted;
1415
+
1416
+
1417
+ EmitToStreamer (*OutStreamer, MCInstBuilder (AArch64::LDRSWroX)
1418
+ .addReg (AArch64::X16)
1419
+ .addReg (AArch64::X17)
1420
+ .addReg (AArch64::X16)
1421
+ .addImm (0 )
1422
+ .addImm (1 ));
1423
+ ++InstsEmitted;
1424
+
1425
+ MCSymbol *AdrLabel = MF->getContext ().createTempSymbol ();
1426
+ auto *AdrLabelE = MCSymbolRefExpr::create (AdrLabel, MF->getContext ());
1427
+ AArch64FI->setJumpTableEntryInfo (JTI, 4 , AdrLabel);
1428
+
1429
+ OutStreamer->emitLabel (AdrLabel);
1430
+ EmitToStreamer (*OutStreamer, MCInstBuilder (AArch64::ADR)
1431
+ .addReg (AArch64::X17)
1432
+ .addExpr (AdrLabelE));
1433
+ ++InstsEmitted;
1434
+
1435
+ EmitToStreamer (*OutStreamer, MCInstBuilder (AArch64::ADDXrs)
1436
+ .addReg (AArch64::X16)
1437
+ .addReg (AArch64::X17)
1438
+ .addReg (AArch64::X16)
1439
+ .addImm (0 ));
1440
+ ++InstsEmitted;
1441
+
1442
+ EmitToStreamer (*OutStreamer, MCInstBuilder (AArch64::BR)
1443
+ .addReg (AArch64::X16));
1444
+ ++InstsEmitted;
1445
+
1446
+ assert (STI->getInstrInfo ()->getInstSizeInBytes (MI) >= InstsEmitted * 4 );
1447
+ }
1448
+
1449
+
1313
1450
void AArch64AsmPrinter::LowerMOPS (llvm::MCStreamer &OutStreamer,
1314
1451
const llvm::MachineInstr &MI) {
1315
1452
unsigned Opcode = MI.getOpcode ();
@@ -2177,6 +2314,10 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
2177
2314
LowerJumpTableDest (*OutStreamer, *MI);
2178
2315
return ;
2179
2316
2317
+ case AArch64::BR_JumpTable:
2318
+ LowerHardenedBRJumpTable (*MI);
2319
+ return ;
2320
+
2180
2321
case AArch64::FMOVH0:
2181
2322
case AArch64::FMOVS0:
2182
2323
case AArch64::FMOVD0:
0 commit comments