@@ -5412,21 +5412,195 @@ AArch64InstrInfo::findRegisterToSaveLRTo(const outliner::Candidate &C) const {
5412
5412
return 0u ;
5413
5413
}
5414
5414
5415
- outliner::OutlinedFunction
5416
- AArch64InstrInfo::getOutliningCandidateInfo (
5415
+ static bool
5416
+ outliningCandidatesSigningScopeConsensus (const outliner::Candidate &a,
5417
+ const outliner::Candidate &b) {
5418
+ const Function &Fa = a.getMF ()->getFunction ();
5419
+ const Function &Fb = b.getMF ()->getFunction ();
5420
+
5421
+ // If none of the functions have the "sign-return-address" attribute their
5422
+ // signing behaviour is equal
5423
+ if (!Fa.hasFnAttribute (" sign-return-address" ) &&
5424
+ !Fb.hasFnAttribute (" sign-return-address" )) {
5425
+ return true ;
5426
+ }
5427
+
5428
+ // If both functions have the "sign-return-address" attribute their signing
5429
+ // behaviour is equal, if the values of the attributes are equal
5430
+ if (Fa.hasFnAttribute (" sign-return-address" ) &&
5431
+ Fb.hasFnAttribute (" sign-return-address" )) {
5432
+ StringRef ScopeA =
5433
+ Fa.getFnAttribute (" sign-return-address" ).getValueAsString ();
5434
+ StringRef ScopeB =
5435
+ Fb.getFnAttribute (" sign-return-address" ).getValueAsString ();
5436
+ return ScopeA.equals (ScopeB);
5437
+ }
5438
+
5439
+ // If function B doesn't have the "sign-return-address" attribute but A does,
5440
+ // the functions' signing behaviour is equal if A's value for
5441
+ // "sign-return-address" is "none" and vice versa.
5442
+ if (Fa.hasFnAttribute (" sign-return-address" )) {
5443
+ StringRef ScopeA =
5444
+ Fa.getFnAttribute (" sign-return-address" ).getValueAsString ();
5445
+ return ScopeA.equals (" none" );
5446
+ }
5447
+
5448
+ if (Fb.hasFnAttribute (" sign-return-address" )) {
5449
+ StringRef ScopeB =
5450
+ Fb.getFnAttribute (" sign-return-address" ).getValueAsString ();
5451
+ return ScopeB.equals (" none" );
5452
+ }
5453
+
5454
+ llvm_unreachable (" Unkown combination of sign-return-address attributes" );
5455
+ }
5456
+
5457
+ static bool
5458
+ outliningCandidatesSigningKeyConsensus (const outliner::Candidate &a,
5459
+ const outliner::Candidate &b) {
5460
+ const Function &Fa = a.getMF ()->getFunction ();
5461
+ const Function &Fb = b.getMF ()->getFunction ();
5462
+
5463
+ // If none of the functions have the "sign-return-address-key" attribute
5464
+ // their keys are equal
5465
+ if (!Fa.hasFnAttribute (" sign-return-address-key" ) &&
5466
+ !Fb.hasFnAttribute (" sign-return-address-key" )) {
5467
+ return true ;
5468
+ }
5469
+
5470
+ // If both functions have the "sign-return-address-key" attribute their
5471
+ // keys are equal if the values of "sign-return-address-key" are equal
5472
+ if (Fa.hasFnAttribute (" sign-return-address-key" ) &&
5473
+ Fb.hasFnAttribute (" sign-return-address-key" )) {
5474
+ StringRef KeyA =
5475
+ Fa.getFnAttribute (" sign-return-address-key" ).getValueAsString ();
5476
+ StringRef KeyB =
5477
+ Fb.getFnAttribute (" sign-return-address-key" ).getValueAsString ();
5478
+ return KeyA.equals (KeyB);
5479
+ }
5480
+
5481
+ // If B doesn't have the "sign-return-address-key" attribute, both keys are
5482
+ // equal, if function a has the default key (a_key)
5483
+ if (Fa.hasFnAttribute (" sign-return-address-key" )) {
5484
+ StringRef KeyA =
5485
+ Fa.getFnAttribute (" sign-return-address-key" ).getValueAsString ();
5486
+ return KeyA.equals_lower (" a_key" );
5487
+ }
5488
+
5489
+ if (Fb.hasFnAttribute (" sign-return-address-key" )) {
5490
+ StringRef KeyB =
5491
+ Fb.getFnAttribute (" sign-return-address-key" ).getValueAsString ();
5492
+ return KeyB.equals_lower (" a_key" );
5493
+ }
5494
+
5495
+ llvm_unreachable (" Unkown combination of sign-return-address-key attributes" );
5496
+ }
5497
+
5498
+ static bool outliningCandidatesV8_3OpsConsensus (const outliner::Candidate &a,
5499
+ const outliner::Candidate &b) {
5500
+ const AArch64Subtarget &SubtargetA =
5501
+ a.getMF ()->getSubtarget <AArch64Subtarget>();
5502
+ const AArch64Subtarget &SubtargetB =
5503
+ b.getMF ()->getSubtarget <AArch64Subtarget>();
5504
+ return SubtargetA.hasV8_3aOps () == SubtargetB.hasV8_3aOps ();
5505
+ }
5506
+
5507
+ outliner::OutlinedFunction AArch64InstrInfo::getOutliningCandidateInfo (
5417
5508
std::vector<outliner::Candidate> &RepeatedSequenceLocs) const {
5418
5509
outliner::Candidate &FirstCand = RepeatedSequenceLocs[0 ];
5419
5510
unsigned SequenceSize =
5420
5511
std::accumulate (FirstCand.front (), std::next (FirstCand.back ()), 0 ,
5421
5512
[this ](unsigned Sum, const MachineInstr &MI) {
5422
5513
return Sum + getInstSizeInBytes (MI);
5423
5514
});
5515
+ unsigned NumBytesToCreateFrame = 0 ;
5516
+
5517
+ // We only allow outlining for functions having exactly matching return
5518
+ // address signing attributes, i.e., all share the same value for the
5519
+ // attribute "sign-return-address" and all share the same type of key they
5520
+ // are signed with.
5521
+ // Additionally we require all functions to simultaniously either support
5522
+ // v8.3a features or not. Otherwise an outlined function could get signed
5523
+ // using dedicated v8.3 instructions and a call from a function that doesn't
5524
+ // support v8.3 instructions would therefore be invalid.
5525
+ if (std::adjacent_find (
5526
+ RepeatedSequenceLocs.begin (), RepeatedSequenceLocs.end (),
5527
+ [](const outliner::Candidate &a, const outliner::Candidate &b) {
5528
+ // Return true if a and b are non-equal w.r.t. return address
5529
+ // signing or support of v8.3a features
5530
+ if (outliningCandidatesSigningScopeConsensus (a, b) &&
5531
+ outliningCandidatesSigningKeyConsensus (a, b) &&
5532
+ outliningCandidatesV8_3OpsConsensus (a, b)) {
5533
+ return false ;
5534
+ }
5535
+ return true ;
5536
+ }) != RepeatedSequenceLocs.end ()) {
5537
+ return outliner::OutlinedFunction ();
5538
+ }
5539
+
5540
+ // Since at this point all candidates agree on their return address signing
5541
+ // picking just one is fine. If the candidate functions potentially sign their
5542
+ // return addresses, the outlined function should do the same. Note that in
5543
+ // the case of "sign-return-address"="non-leaf" this is an assumption: It is
5544
+ // not certainly true that the outlined function will have to sign its return
5545
+ // address but this decision is made later, when the decision to outline
5546
+ // has already been made.
5547
+ // The same holds for the number of additional instructions we need: On
5548
+ // v8.3a RET can be replaced by RETAA/RETAB and no AUT instruction is
5549
+ // necessary. However, at this point we don't know if the outlined function
5550
+ // will have a RET instruction so we assume the worst.
5551
+ const Function &FCF = FirstCand.getMF ()->getFunction ();
5552
+ const TargetRegisterInfo &TRI = getRegisterInfo ();
5553
+ if (FCF.hasFnAttribute (" sign-return-address" )) {
5554
+ // One PAC and one AUT instructions
5555
+ NumBytesToCreateFrame += 8 ;
5556
+
5557
+ // We have to check if sp modifying instructions would get outlined.
5558
+ // If so we only allow outlining if sp is unchanged overall, so matching
5559
+ // sub and add instructions are okay to outline, all other sp modifications
5560
+ // are not
5561
+ auto hasIllegalSPModification = [&TRI](outliner::Candidate &C) {
5562
+ int SPValue = 0 ;
5563
+ MachineBasicBlock::iterator MBBI = C.front ();
5564
+ for (;;) {
5565
+ if (MBBI->modifiesRegister (AArch64::SP, &TRI)) {
5566
+ switch (MBBI->getOpcode ()) {
5567
+ case AArch64::ADDXri:
5568
+ case AArch64::ADDWri:
5569
+ assert (MBBI->getNumOperands () == 4 && " Wrong number of operands" );
5570
+ assert (MBBI->getOperand (2 ).isImm () &&
5571
+ " Expected operand to be immediate" );
5572
+ SPValue += MBBI->getOperand (2 ).getImm ();
5573
+ break ;
5574
+ case AArch64::SUBXri:
5575
+ case AArch64::SUBWri:
5576
+ assert (MBBI->getNumOperands () == 4 && " Wrong number of operands" );
5577
+ assert (MBBI->getOperand (2 ).isImm () &&
5578
+ " Expected operand to be immediate" );
5579
+ SPValue -= MBBI->getOperand (2 ).getImm ();
5580
+ break ;
5581
+ default :
5582
+ return true ;
5583
+ }
5584
+ }
5585
+ if (MBBI == C.back ())
5586
+ break ;
5587
+ ++MBBI;
5588
+ }
5589
+ if (SPValue)
5590
+ return true ;
5591
+ return false ;
5592
+ };
5593
+ // Remove candidates with illegal stack modifying instructions
5594
+ RepeatedSequenceLocs.erase (std::remove_if (RepeatedSequenceLocs.begin (),
5595
+ RepeatedSequenceLocs.end (),
5596
+ hasIllegalSPModification),
5597
+ RepeatedSequenceLocs.end ());
5598
+ }
5424
5599
5425
5600
// Properties about candidate MBBs that hold for all of them.
5426
5601
unsigned FlagsSetInAll = 0xF ;
5427
5602
5428
5603
// Compute liveness information for each candidate, and set FlagsSetInAll.
5429
- const TargetRegisterInfo &TRI = getRegisterInfo ();
5430
5604
std::for_each (RepeatedSequenceLocs.begin (), RepeatedSequenceLocs.end (),
5431
5605
[&FlagsSetInAll](outliner::Candidate &C) {
5432
5606
FlagsSetInAll &= C.Flags ;
@@ -5482,7 +5656,7 @@ AArch64InstrInfo::getOutliningCandidateInfo(
5482
5656
};
5483
5657
5484
5658
unsigned FrameID = MachineOutlinerDefault;
5485
- unsigned NumBytesToCreateFrame = 4 ;
5659
+ NumBytesToCreateFrame + = 4 ;
5486
5660
5487
5661
bool HasBTI = any_of (RepeatedSequenceLocs, [](outliner::Candidate &C) {
5488
5662
return C.getMF ()->getFunction ().hasFnAttribute (" branch-target-enforcement" );
@@ -5751,6 +5925,19 @@ AArch64InstrInfo::getOutliningType(MachineBasicBlock::iterator &MIT,
5751
5925
MachineFunction *MF = MBB->getParent ();
5752
5926
AArch64FunctionInfo *FuncInfo = MF->getInfo <AArch64FunctionInfo>();
5753
5927
5928
+ // Don't outline anything used for return address signing. The outlined
5929
+ // function will get signed later if needed
5930
+ switch (MI.getOpcode ()) {
5931
+ case AArch64::PACIASP:
5932
+ case AArch64::PACIBSP:
5933
+ case AArch64::AUTIASP:
5934
+ case AArch64::AUTIBSP:
5935
+ case AArch64::RETAA:
5936
+ case AArch64::RETAB:
5937
+ case AArch64::EMITBKEY:
5938
+ return outliner::InstrType::Illegal;
5939
+ }
5940
+
5754
5941
// Don't outline LOHs.
5755
5942
if (FuncInfo->getLOHRelated ().count (&MI))
5756
5943
return outliner::InstrType::Illegal;
@@ -5903,6 +6090,59 @@ void AArch64InstrInfo::fixupPostOutline(MachineBasicBlock &MBB) const {
5903
6090
}
5904
6091
}
5905
6092
6093
+ static void signOutlinedFunction (MachineFunction &MF, MachineBasicBlock &MBB,
6094
+ bool ShouldSignReturnAddr,
6095
+ bool ShouldSignReturnAddrWithAKey) {
6096
+ if (ShouldSignReturnAddr) {
6097
+ MachineBasicBlock::iterator MBBPAC = MBB.begin ();
6098
+ MachineBasicBlock::iterator MBBAUT = MBB.getFirstTerminator ();
6099
+ const AArch64Subtarget &Subtarget = MF.getSubtarget <AArch64Subtarget>();
6100
+ const TargetInstrInfo *TII = Subtarget.getInstrInfo ();
6101
+ DebugLoc DL;
6102
+
6103
+ if (MBBAUT != MBB.end ())
6104
+ DL = MBBAUT->getDebugLoc ();
6105
+
6106
+ // At the very beginning of the basic block we insert the following
6107
+ // depending on the key type
6108
+ //
6109
+ // a_key: b_key:
6110
+ // PACIASP EMITBKEY
6111
+ // CFI_INSTRUCTION PACIBSP
6112
+ // CFI_INSTRUCTION
6113
+ if (ShouldSignReturnAddrWithAKey) {
6114
+ BuildMI (MBB, MBBPAC, DebugLoc (), TII->get (AArch64::PACIASP))
6115
+ .setMIFlag (MachineInstr::FrameSetup);
6116
+ } else {
6117
+ BuildMI (MBB, MBBPAC, DebugLoc (), TII->get (AArch64::EMITBKEY))
6118
+ .setMIFlag (MachineInstr::FrameSetup);
6119
+ BuildMI (MBB, MBBPAC, DebugLoc (), TII->get (AArch64::PACIBSP))
6120
+ .setMIFlag (MachineInstr::FrameSetup);
6121
+ }
6122
+ unsigned CFIIndex =
6123
+ MF.addFrameInst (MCCFIInstruction::createNegateRAState (nullptr ));
6124
+ BuildMI (MBB, MBBPAC, DebugLoc (), TII->get (AArch64::CFI_INSTRUCTION))
6125
+ .addCFIIndex (CFIIndex)
6126
+ .setMIFlags (MachineInstr::FrameSetup);
6127
+
6128
+ // If v8.3a features are available we can replace a RET instruction by
6129
+ // RETAA or RETAB and omit the AUT instructions
6130
+ if (Subtarget.hasV8_3aOps () && MBBAUT != MBB.end () &&
6131
+ MBBAUT->getOpcode () == AArch64::RET) {
6132
+ BuildMI (MBB, MBBAUT, DL,
6133
+ TII->get (ShouldSignReturnAddrWithAKey ? AArch64::RETAA
6134
+ : AArch64::RETAB))
6135
+ .copyImplicitOps (*MBBAUT);
6136
+ MBB.erase (MBBAUT);
6137
+ } else {
6138
+ BuildMI (MBB, MBBAUT, DL,
6139
+ TII->get (ShouldSignReturnAddrWithAKey ? AArch64::AUTIASP
6140
+ : AArch64::AUTIBSP))
6141
+ .setMIFlag (MachineInstr::FrameDestroy);
6142
+ }
6143
+ }
6144
+ }
6145
+
5906
6146
void AArch64InstrInfo::buildOutlinedFrame (
5907
6147
MachineBasicBlock &MBB, MachineFunction &MF,
5908
6148
const outliner::OutlinedFunction &OF) const {
@@ -5918,23 +6158,28 @@ void AArch64InstrInfo::buildOutlinedFrame(
5918
6158
TailOpcode = AArch64::TCRETURNriALL;
5919
6159
}
5920
6160
MachineInstr *TC = BuildMI (MF, DebugLoc (), get (TailOpcode))
5921
- .add (Call->getOperand (0 ))
5922
- .addImm (0 );
6161
+ .add (Call->getOperand (0 ))
6162
+ .addImm (0 );
5923
6163
MBB.insert (MBB.end (), TC);
5924
6164
Call->eraseFromParent ();
5925
6165
}
5926
6166
6167
+ bool IsLeafFunction = true ;
6168
+
5927
6169
// Is there a call in the outlined range?
5928
- auto IsNonTailCall = [](MachineInstr &MI) {
6170
+ auto IsNonTailCall = [](const MachineInstr &MI) {
5929
6171
return MI.isCall () && !MI.isReturn ();
5930
6172
};
6173
+
5931
6174
if (std::any_of (MBB.instr_begin (), MBB.instr_end (), IsNonTailCall)) {
5932
6175
// Fix up the instructions in the range, since we're going to modify the
5933
6176
// stack.
5934
6177
assert (OF.FrameConstructionID != MachineOutlinerDefault &&
5935
6178
" Can only fix up stack references once" );
5936
6179
fixupPostOutline (MBB);
5937
6180
6181
+ IsLeafFunction = false ;
6182
+
5938
6183
// LR has to be a live in so that we can save it.
5939
6184
MBB.addLiveIn (AArch64::LR);
5940
6185
@@ -5981,16 +6226,47 @@ void AArch64InstrInfo::buildOutlinedFrame(
5981
6226
Et = MBB.insert (Et, LDRXpost);
5982
6227
}
5983
6228
6229
+ // If a bunch of candidates reach this point they must agree on their return
6230
+ // address signing. It is therefore enough to just consider the signing
6231
+ // behaviour of one of them
6232
+ const Function &CF = OF.Candidates .front ().getMF ()->getFunction ();
6233
+ bool ShouldSignReturnAddr = false ;
6234
+ if (CF.hasFnAttribute (" sign-return-address" )) {
6235
+ StringRef Scope =
6236
+ CF.getFnAttribute (" sign-return-address" ).getValueAsString ();
6237
+ if (Scope.equals (" all" ))
6238
+ ShouldSignReturnAddr = true ;
6239
+ else if (Scope.equals (" non-leaf" ) && !IsLeafFunction)
6240
+ ShouldSignReturnAddr = true ;
6241
+ }
6242
+
6243
+ // a_key is the default
6244
+ bool ShouldSignReturnAddrWithAKey = true ;
6245
+ if (CF.hasFnAttribute (" sign-return-address-key" )) {
6246
+ const StringRef Key =
6247
+ CF.getFnAttribute (" sign-return-address-key" ).getValueAsString ();
6248
+ // Key can either be a_key or b_key
6249
+ assert ((Key.equals_lower (" a_key" ) || Key.equals_lower (" b_key" )) &&
6250
+ " Return address signing key must be either a_key or b_key" );
6251
+ ShouldSignReturnAddrWithAKey = Key.equals_lower (" a_key" );
6252
+ }
6253
+
5984
6254
// If this is a tail call outlined function, then there's already a return.
5985
6255
if (OF.FrameConstructionID == MachineOutlinerTailCall ||
5986
- OF.FrameConstructionID == MachineOutlinerThunk)
6256
+ OF.FrameConstructionID == MachineOutlinerThunk) {
6257
+ signOutlinedFunction (MF, MBB, ShouldSignReturnAddr,
6258
+ ShouldSignReturnAddrWithAKey);
5987
6259
return ;
6260
+ }
5988
6261
5989
6262
// It's not a tail call, so we have to insert the return ourselves.
5990
6263
MachineInstr *ret = BuildMI (MF, DebugLoc (), get (AArch64::RET))
5991
6264
.addReg (AArch64::LR, RegState::Undef);
5992
6265
MBB.insert (MBB.end (), ret);
5993
6266
6267
+ signOutlinedFunction (MF, MBB, ShouldSignReturnAddr,
6268
+ ShouldSignReturnAddrWithAKey);
6269
+
5994
6270
// Did we have to modify the stack by saving the link register?
5995
6271
if (OF.FrameConstructionID != MachineOutlinerDefault)
5996
6272
return ;
0 commit comments