@@ -5412,21 +5412,199 @@ 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
+
5599
+ // If the sequence doesn't have enough candidates left, then we're done.
5600
+ if (RepeatedSequenceLocs.size () < 2 )
5601
+ return outliner::OutlinedFunction ();
5602
+ }
5424
5603
5425
5604
// Properties about candidate MBBs that hold for all of them.
5426
5605
unsigned FlagsSetInAll = 0xF ;
5427
5606
5428
5607
// Compute liveness information for each candidate, and set FlagsSetInAll.
5429
- const TargetRegisterInfo &TRI = getRegisterInfo ();
5430
5608
std::for_each (RepeatedSequenceLocs.begin (), RepeatedSequenceLocs.end (),
5431
5609
[&FlagsSetInAll](outliner::Candidate &C) {
5432
5610
FlagsSetInAll &= C.Flags ;
@@ -5482,7 +5660,7 @@ AArch64InstrInfo::getOutliningCandidateInfo(
5482
5660
};
5483
5661
5484
5662
unsigned FrameID = MachineOutlinerDefault;
5485
- unsigned NumBytesToCreateFrame = 4 ;
5663
+ NumBytesToCreateFrame + = 4 ;
5486
5664
5487
5665
bool HasBTI = any_of (RepeatedSequenceLocs, [](outliner::Candidate &C) {
5488
5666
return C.getMF ()->getFunction ().hasFnAttribute (" branch-target-enforcement" );
@@ -5751,6 +5929,19 @@ AArch64InstrInfo::getOutliningType(MachineBasicBlock::iterator &MIT,
5751
5929
MachineFunction *MF = MBB->getParent ();
5752
5930
AArch64FunctionInfo *FuncInfo = MF->getInfo <AArch64FunctionInfo>();
5753
5931
5932
+ // Don't outline anything used for return address signing. The outlined
5933
+ // function will get signed later if needed
5934
+ switch (MI.getOpcode ()) {
5935
+ case AArch64::PACIASP:
5936
+ case AArch64::PACIBSP:
5937
+ case AArch64::AUTIASP:
5938
+ case AArch64::AUTIBSP:
5939
+ case AArch64::RETAA:
5940
+ case AArch64::RETAB:
5941
+ case AArch64::EMITBKEY:
5942
+ return outliner::InstrType::Illegal;
5943
+ }
5944
+
5754
5945
// Don't outline LOHs.
5755
5946
if (FuncInfo->getLOHRelated ().count (&MI))
5756
5947
return outliner::InstrType::Illegal;
@@ -5903,6 +6094,59 @@ void AArch64InstrInfo::fixupPostOutline(MachineBasicBlock &MBB) const {
5903
6094
}
5904
6095
}
5905
6096
6097
+ static void signOutlinedFunction (MachineFunction &MF, MachineBasicBlock &MBB,
6098
+ bool ShouldSignReturnAddr,
6099
+ bool ShouldSignReturnAddrWithAKey) {
6100
+ if (ShouldSignReturnAddr) {
6101
+ MachineBasicBlock::iterator MBBPAC = MBB.begin ();
6102
+ MachineBasicBlock::iterator MBBAUT = MBB.getFirstTerminator ();
6103
+ const AArch64Subtarget &Subtarget = MF.getSubtarget <AArch64Subtarget>();
6104
+ const TargetInstrInfo *TII = Subtarget.getInstrInfo ();
6105
+ DebugLoc DL;
6106
+
6107
+ if (MBBAUT != MBB.end ())
6108
+ DL = MBBAUT->getDebugLoc ();
6109
+
6110
+ // At the very beginning of the basic block we insert the following
6111
+ // depending on the key type
6112
+ //
6113
+ // a_key: b_key:
6114
+ // PACIASP EMITBKEY
6115
+ // CFI_INSTRUCTION PACIBSP
6116
+ // CFI_INSTRUCTION
6117
+ if (ShouldSignReturnAddrWithAKey) {
6118
+ BuildMI (MBB, MBBPAC, DebugLoc (), TII->get (AArch64::PACIASP))
6119
+ .setMIFlag (MachineInstr::FrameSetup);
6120
+ } else {
6121
+ BuildMI (MBB, MBBPAC, DebugLoc (), TII->get (AArch64::EMITBKEY))
6122
+ .setMIFlag (MachineInstr::FrameSetup);
6123
+ BuildMI (MBB, MBBPAC, DebugLoc (), TII->get (AArch64::PACIBSP))
6124
+ .setMIFlag (MachineInstr::FrameSetup);
6125
+ }
6126
+ unsigned CFIIndex =
6127
+ MF.addFrameInst (MCCFIInstruction::createNegateRAState (nullptr ));
6128
+ BuildMI (MBB, MBBPAC, DebugLoc (), TII->get (AArch64::CFI_INSTRUCTION))
6129
+ .addCFIIndex (CFIIndex)
6130
+ .setMIFlags (MachineInstr::FrameSetup);
6131
+
6132
+ // If v8.3a features are available we can replace a RET instruction by
6133
+ // RETAA or RETAB and omit the AUT instructions
6134
+ if (Subtarget.hasV8_3aOps () && MBBAUT != MBB.end () &&
6135
+ MBBAUT->getOpcode () == AArch64::RET) {
6136
+ BuildMI (MBB, MBBAUT, DL,
6137
+ TII->get (ShouldSignReturnAddrWithAKey ? AArch64::RETAA
6138
+ : AArch64::RETAB))
6139
+ .copyImplicitOps (*MBBAUT);
6140
+ MBB.erase (MBBAUT);
6141
+ } else {
6142
+ BuildMI (MBB, MBBAUT, DL,
6143
+ TII->get (ShouldSignReturnAddrWithAKey ? AArch64::AUTIASP
6144
+ : AArch64::AUTIBSP))
6145
+ .setMIFlag (MachineInstr::FrameDestroy);
6146
+ }
6147
+ }
6148
+ }
6149
+
5906
6150
void AArch64InstrInfo::buildOutlinedFrame (
5907
6151
MachineBasicBlock &MBB, MachineFunction &MF,
5908
6152
const outliner::OutlinedFunction &OF) const {
@@ -5918,23 +6162,28 @@ void AArch64InstrInfo::buildOutlinedFrame(
5918
6162
TailOpcode = AArch64::TCRETURNriALL;
5919
6163
}
5920
6164
MachineInstr *TC = BuildMI (MF, DebugLoc (), get (TailOpcode))
5921
- .add (Call->getOperand (0 ))
5922
- .addImm (0 );
6165
+ .add (Call->getOperand (0 ))
6166
+ .addImm (0 );
5923
6167
MBB.insert (MBB.end (), TC);
5924
6168
Call->eraseFromParent ();
5925
6169
}
5926
6170
6171
+ bool IsLeafFunction = true ;
6172
+
5927
6173
// Is there a call in the outlined range?
5928
- auto IsNonTailCall = [](MachineInstr &MI) {
6174
+ auto IsNonTailCall = [](const MachineInstr &MI) {
5929
6175
return MI.isCall () && !MI.isReturn ();
5930
6176
};
6177
+
5931
6178
if (std::any_of (MBB.instr_begin (), MBB.instr_end (), IsNonTailCall)) {
5932
6179
// Fix up the instructions in the range, since we're going to modify the
5933
6180
// stack.
5934
6181
assert (OF.FrameConstructionID != MachineOutlinerDefault &&
5935
6182
" Can only fix up stack references once" );
5936
6183
fixupPostOutline (MBB);
5937
6184
6185
+ IsLeafFunction = false ;
6186
+
5938
6187
// LR has to be a live in so that we can save it.
5939
6188
MBB.addLiveIn (AArch64::LR);
5940
6189
@@ -5981,16 +6230,47 @@ void AArch64InstrInfo::buildOutlinedFrame(
5981
6230
Et = MBB.insert (Et, LDRXpost);
5982
6231
}
5983
6232
6233
+ // If a bunch of candidates reach this point they must agree on their return
6234
+ // address signing. It is therefore enough to just consider the signing
6235
+ // behaviour of one of them
6236
+ const Function &CF = OF.Candidates .front ().getMF ()->getFunction ();
6237
+ bool ShouldSignReturnAddr = false ;
6238
+ if (CF.hasFnAttribute (" sign-return-address" )) {
6239
+ StringRef Scope =
6240
+ CF.getFnAttribute (" sign-return-address" ).getValueAsString ();
6241
+ if (Scope.equals (" all" ))
6242
+ ShouldSignReturnAddr = true ;
6243
+ else if (Scope.equals (" non-leaf" ) && !IsLeafFunction)
6244
+ ShouldSignReturnAddr = true ;
6245
+ }
6246
+
6247
+ // a_key is the default
6248
+ bool ShouldSignReturnAddrWithAKey = true ;
6249
+ if (CF.hasFnAttribute (" sign-return-address-key" )) {
6250
+ const StringRef Key =
6251
+ CF.getFnAttribute (" sign-return-address-key" ).getValueAsString ();
6252
+ // Key can either be a_key or b_key
6253
+ assert ((Key.equals_lower (" a_key" ) || Key.equals_lower (" b_key" )) &&
6254
+ " Return address signing key must be either a_key or b_key" );
6255
+ ShouldSignReturnAddrWithAKey = Key.equals_lower (" a_key" );
6256
+ }
6257
+
5984
6258
// If this is a tail call outlined function, then there's already a return.
5985
6259
if (OF.FrameConstructionID == MachineOutlinerTailCall ||
5986
- OF.FrameConstructionID == MachineOutlinerThunk)
6260
+ OF.FrameConstructionID == MachineOutlinerThunk) {
6261
+ signOutlinedFunction (MF, MBB, ShouldSignReturnAddr,
6262
+ ShouldSignReturnAddrWithAKey);
5987
6263
return ;
6264
+ }
5988
6265
5989
6266
// It's not a tail call, so we have to insert the return ourselves.
5990
6267
MachineInstr *ret = BuildMI (MF, DebugLoc (), get (AArch64::RET))
5991
6268
.addReg (AArch64::LR, RegState::Undef);
5992
6269
MBB.insert (MBB.end (), ret);
5993
6270
6271
+ signOutlinedFunction (MF, MBB, ShouldSignReturnAddr,
6272
+ ShouldSignReturnAddrWithAKey);
6273
+
5994
6274
// Did we have to modify the stack by saving the link register?
5995
6275
if (OF.FrameConstructionID != MachineOutlinerDefault)
5996
6276
return ;
0 commit comments