@@ -5412,21 +5412,199 @@ AArch64InstrInfo::findRegisterToSaveLRTo(const outliner::Candidate &C) const {
54125412 return 0u ;
54135413}
54145414
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 (
54175508 std::vector<outliner::Candidate> &RepeatedSequenceLocs) const {
54185509 outliner::Candidate &FirstCand = RepeatedSequenceLocs[0 ];
54195510 unsigned SequenceSize =
54205511 std::accumulate (FirstCand.front (), std::next (FirstCand.back ()), 0 ,
54215512 [this ](unsigned Sum, const MachineInstr &MI) {
54225513 return Sum + getInstSizeInBytes (MI);
54235514 });
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+ }
54245603
54255604 // Properties about candidate MBBs that hold for all of them.
54265605 unsigned FlagsSetInAll = 0xF ;
54275606
54285607 // Compute liveness information for each candidate, and set FlagsSetInAll.
5429- const TargetRegisterInfo &TRI = getRegisterInfo ();
54305608 std::for_each (RepeatedSequenceLocs.begin (), RepeatedSequenceLocs.end (),
54315609 [&FlagsSetInAll](outliner::Candidate &C) {
54325610 FlagsSetInAll &= C.Flags ;
@@ -5482,7 +5660,7 @@ AArch64InstrInfo::getOutliningCandidateInfo(
54825660 };
54835661
54845662 unsigned FrameID = MachineOutlinerDefault;
5485- unsigned NumBytesToCreateFrame = 4 ;
5663+ NumBytesToCreateFrame + = 4 ;
54865664
54875665 bool HasBTI = any_of (RepeatedSequenceLocs, [](outliner::Candidate &C) {
54885666 return C.getMF ()->getFunction ().hasFnAttribute (" branch-target-enforcement" );
@@ -5751,6 +5929,19 @@ AArch64InstrInfo::getOutliningType(MachineBasicBlock::iterator &MIT,
57515929 MachineFunction *MF = MBB->getParent ();
57525930 AArch64FunctionInfo *FuncInfo = MF->getInfo <AArch64FunctionInfo>();
57535931
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+
57545945 // Don't outline LOHs.
57555946 if (FuncInfo->getLOHRelated ().count (&MI))
57565947 return outliner::InstrType::Illegal;
@@ -5903,6 +6094,59 @@ void AArch64InstrInfo::fixupPostOutline(MachineBasicBlock &MBB) const {
59036094 }
59046095}
59056096
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+
59066150void AArch64InstrInfo::buildOutlinedFrame (
59076151 MachineBasicBlock &MBB, MachineFunction &MF,
59086152 const outliner::OutlinedFunction &OF) const {
@@ -5918,23 +6162,28 @@ void AArch64InstrInfo::buildOutlinedFrame(
59186162 TailOpcode = AArch64::TCRETURNriALL;
59196163 }
59206164 MachineInstr *TC = BuildMI (MF, DebugLoc (), get (TailOpcode))
5921- .add (Call->getOperand (0 ))
5922- .addImm (0 );
6165+ .add (Call->getOperand (0 ))
6166+ .addImm (0 );
59236167 MBB.insert (MBB.end (), TC);
59246168 Call->eraseFromParent ();
59256169 }
59266170
6171+ bool IsLeafFunction = true ;
6172+
59276173 // Is there a call in the outlined range?
5928- auto IsNonTailCall = [](MachineInstr &MI) {
6174+ auto IsNonTailCall = [](const MachineInstr &MI) {
59296175 return MI.isCall () && !MI.isReturn ();
59306176 };
6177+
59316178 if (std::any_of (MBB.instr_begin (), MBB.instr_end (), IsNonTailCall)) {
59326179 // Fix up the instructions in the range, since we're going to modify the
59336180 // stack.
59346181 assert (OF.FrameConstructionID != MachineOutlinerDefault &&
59356182 " Can only fix up stack references once" );
59366183 fixupPostOutline (MBB);
59376184
6185+ IsLeafFunction = false ;
6186+
59386187 // LR has to be a live in so that we can save it.
59396188 MBB.addLiveIn (AArch64::LR);
59406189
@@ -5981,16 +6230,47 @@ void AArch64InstrInfo::buildOutlinedFrame(
59816230 Et = MBB.insert (Et, LDRXpost);
59826231 }
59836232
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+
59846258 // If this is a tail call outlined function, then there's already a return.
59856259 if (OF.FrameConstructionID == MachineOutlinerTailCall ||
5986- OF.FrameConstructionID == MachineOutlinerThunk)
6260+ OF.FrameConstructionID == MachineOutlinerThunk) {
6261+ signOutlinedFunction (MF, MBB, ShouldSignReturnAddr,
6262+ ShouldSignReturnAddrWithAKey);
59876263 return ;
6264+ }
59886265
59896266 // It's not a tail call, so we have to insert the return ourselves.
59906267 MachineInstr *ret = BuildMI (MF, DebugLoc (), get (AArch64::RET))
59916268 .addReg (AArch64::LR, RegState::Undef);
59926269 MBB.insert (MBB.end (), ret);
59936270
6271+ signOutlinedFunction (MF, MBB, ShouldSignReturnAddr,
6272+ ShouldSignReturnAddrWithAKey);
6273+
59946274 // Did we have to modify the stack by saving the link register?
59956275 if (OF.FrameConstructionID != MachineOutlinerDefault)
59966276 return ;
0 commit comments