330 lines · cpp
1//===-- SwiftErrorValueTracking.cpp --------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This implements a limited mem2reg-like analysis to promote uses of function10// arguments and allocas marked with swiftalloc from memory into virtual11// registers tracked by this class.12//13//===----------------------------------------------------------------------===//14 15#include "llvm/CodeGen/SwiftErrorValueTracking.h"16#include "llvm/ADT/PostOrderIterator.h"17#include "llvm/CodeGen/MachineInstrBuilder.h"18#include "llvm/CodeGen/MachineRegisterInfo.h"19#include "llvm/CodeGen/TargetInstrInfo.h"20#include "llvm/CodeGen/TargetLowering.h"21#include "llvm/IR/Value.h"22 23using namespace llvm;24 25Register SwiftErrorValueTracking::getOrCreateVReg(const MachineBasicBlock *MBB,26 const Value *Val) {27 auto Key = std::make_pair(MBB, Val);28 auto It = VRegDefMap.find(Key);29 // If this is the first use of this swifterror value in this basic block,30 // create a new virtual register.31 // After we processed all basic blocks we will satisfy this "upwards exposed32 // use" by inserting a copy or phi at the beginning of this block.33 if (It == VRegDefMap.end()) {34 auto &DL = MF->getDataLayout();35 const TargetRegisterClass *RC = TLI->getRegClassFor(TLI->getPointerTy(DL));36 auto VReg = MF->getRegInfo().createVirtualRegister(RC);37 VRegDefMap[Key] = VReg;38 VRegUpwardsUse[Key] = VReg;39 return VReg;40 } else41 return It->second;42}43 44void SwiftErrorValueTracking::setCurrentVReg(const MachineBasicBlock *MBB,45 const Value *Val, Register VReg) {46 VRegDefMap[std::make_pair(MBB, Val)] = VReg;47}48 49Register SwiftErrorValueTracking::getOrCreateVRegDefAt(50 const Instruction *I, const MachineBasicBlock *MBB, const Value *Val) {51 auto Key = PointerIntPair<const Instruction *, 1, bool>(I, true);52 auto It = VRegDefUses.find(Key);53 if (It != VRegDefUses.end())54 return It->second;55 56 auto &DL = MF->getDataLayout();57 const TargetRegisterClass *RC = TLI->getRegClassFor(TLI->getPointerTy(DL));58 Register VReg = MF->getRegInfo().createVirtualRegister(RC);59 VRegDefUses[Key] = VReg;60 setCurrentVReg(MBB, Val, VReg);61 return VReg;62}63 64Register SwiftErrorValueTracking::getOrCreateVRegUseAt(65 const Instruction *I, const MachineBasicBlock *MBB, const Value *Val) {66 auto Key = PointerIntPair<const Instruction *, 1, bool>(I, false);67 auto It = VRegDefUses.find(Key);68 if (It != VRegDefUses.end())69 return It->second;70 71 Register VReg = getOrCreateVReg(MBB, Val);72 VRegDefUses[Key] = VReg;73 return VReg;74}75 76/// Set up SwiftErrorVals by going through the function. If the function has77/// swifterror argument, it will be the first entry.78void SwiftErrorValueTracking::setFunction(MachineFunction &mf) {79 MF = &mf;80 Fn = &MF->getFunction();81 TLI = MF->getSubtarget().getTargetLowering();82 TII = MF->getSubtarget().getInstrInfo();83 84 if (!TLI->supportSwiftError())85 return;86 87 SwiftErrorVals.clear();88 VRegDefMap.clear();89 VRegUpwardsUse.clear();90 VRegDefUses.clear();91 SwiftErrorArg = nullptr;92 93 // Check if function has a swifterror argument.94 bool HaveSeenSwiftErrorArg = false;95 for (Function::const_arg_iterator AI = Fn->arg_begin(), AE = Fn->arg_end();96 AI != AE; ++AI)97 if (AI->hasSwiftErrorAttr()) {98 assert(!HaveSeenSwiftErrorArg &&99 "Must have only one swifterror parameter");100 (void)HaveSeenSwiftErrorArg; // silence warning.101 HaveSeenSwiftErrorArg = true;102 SwiftErrorArg = &*AI;103 SwiftErrorVals.push_back(&*AI);104 }105 106 for (const auto &LLVMBB : *Fn)107 for (const auto &Inst : LLVMBB) {108 if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(&Inst))109 if (Alloca->isSwiftError())110 SwiftErrorVals.push_back(Alloca);111 }112}113 114bool SwiftErrorValueTracking::createEntriesInEntryBlock(DebugLoc DbgLoc) {115 if (!TLI->supportSwiftError())116 return false;117 118 // We only need to do this when we have swifterror parameter or swifterror119 // alloc.120 if (SwiftErrorVals.empty())121 return false;122 123 MachineBasicBlock *MBB = &*MF->begin();124 auto &DL = MF->getDataLayout();125 auto const *RC = TLI->getRegClassFor(TLI->getPointerTy(DL));126 bool Inserted = false;127 for (const auto *SwiftErrorVal : SwiftErrorVals) {128 // We will always generate a copy from the argument. It is always used at129 // least by the 'return' of the swifterror.130 if (SwiftErrorArg && SwiftErrorArg == SwiftErrorVal)131 continue;132 Register VReg = MF->getRegInfo().createVirtualRegister(RC);133 // Assign Undef to Vreg. We construct MI directly to make sure it works134 // with FastISel.135 BuildMI(*MBB, MBB->getFirstNonPHI(), DbgLoc,136 TII->get(TargetOpcode::IMPLICIT_DEF), VReg);137 138 setCurrentVReg(MBB, SwiftErrorVal, VReg);139 Inserted = true;140 }141 142 return Inserted;143}144 145/// Propagate swifterror values through the machine function CFG.146void SwiftErrorValueTracking::propagateVRegs() {147 if (!TLI->supportSwiftError())148 return;149 150 // We only need to do this when we have swifterror parameter or swifterror151 // alloc.152 if (SwiftErrorVals.empty())153 return;154 155 // For each machine basic block in reverse post order.156 ReversePostOrderTraversal<MachineFunction *> RPOT(MF);157 for (MachineBasicBlock *MBB : RPOT) {158 // For each swifterror value in the function.159 for (const auto *SwiftErrorVal : SwiftErrorVals) {160 auto Key = std::make_pair(MBB, SwiftErrorVal);161 auto UUseIt = VRegUpwardsUse.find(Key);162 auto VRegDefIt = VRegDefMap.find(Key);163 bool UpwardsUse = UUseIt != VRegUpwardsUse.end();164 Register UUseVReg = UpwardsUse ? UUseIt->second : Register();165 bool DownwardDef = VRegDefIt != VRegDefMap.end();166 assert(!(UpwardsUse && !DownwardDef) &&167 "We can't have an upwards use but no downwards def");168 169 // If there is no upwards exposed use and an entry for the swifterror in170 // the def map for this value we don't need to do anything: We already171 // have a downward def for this basic block.172 if (!UpwardsUse && DownwardDef)173 continue;174 175 // Otherwise we either have an upwards exposed use vreg that we need to176 // materialize or need to forward the downward def from predecessors.177 178 // Check whether we have a single vreg def from all predecessors.179 // Otherwise we need a phi.180 SmallVector<std::pair<MachineBasicBlock *, Register>, 4> VRegs;181 SmallPtrSet<const MachineBasicBlock *, 8> Visited;182 for (auto *Pred : MBB->predecessors()) {183 if (!Visited.insert(Pred).second)184 continue;185 VRegs.push_back(std::make_pair(186 Pred, getOrCreateVReg(Pred, SwiftErrorVal)));187 if (Pred != MBB)188 continue;189 // We have a self-edge.190 // If there was no upwards use in this basic block there is now one: the191 // phi needs to use it self.192 if (!UpwardsUse) {193 UpwardsUse = true;194 UUseIt = VRegUpwardsUse.find(Key);195 assert(UUseIt != VRegUpwardsUse.end());196 UUseVReg = UUseIt->second;197 }198 }199 200 // We need a phi node if we have more than one predecessor with different201 // downward defs.202 bool needPHI =203 VRegs.size() >= 1 &&204 llvm::any_of(205 VRegs,206 [&](const std::pair<const MachineBasicBlock *, Register> &V)207 -> bool { return V.second != VRegs[0].second; });208 209 // If there is no upwards exposed used and we don't need a phi just210 // forward the swifterror vreg from the predecessor(s).211 if (!UpwardsUse && !needPHI) {212 assert(!VRegs.empty() &&213 "No predecessors? The entry block should bail out earlier");214 // Just forward the swifterror vreg from the predecessor(s).215 setCurrentVReg(MBB, SwiftErrorVal, VRegs[0].second);216 continue;217 }218 219 auto DLoc = isa<Instruction>(SwiftErrorVal)220 ? cast<Instruction>(SwiftErrorVal)->getDebugLoc()221 : DebugLoc();222 const auto *TII = MF->getSubtarget().getInstrInfo();223 224 // If we don't need a phi create a copy to the upward exposed vreg.225 if (!needPHI) {226 assert(UpwardsUse);227 assert(!VRegs.empty() &&228 "No predecessors? Is the Calling Convention correct?");229 Register DestReg = UUseVReg;230 BuildMI(*MBB, MBB->getFirstNonPHI(), DLoc, TII->get(TargetOpcode::COPY),231 DestReg)232 .addReg(VRegs[0].second);233 continue;234 }235 236 // We need a phi: if there is an upwards exposed use we already have a237 // destination virtual register number otherwise we generate a new one.238 auto &DL = MF->getDataLayout();239 auto const *RC = TLI->getRegClassFor(TLI->getPointerTy(DL));240 Register PHIVReg =241 UpwardsUse ? UUseVReg : MF->getRegInfo().createVirtualRegister(RC);242 MachineInstrBuilder PHI =243 BuildMI(*MBB, MBB->getFirstNonPHI(), DLoc,244 TII->get(TargetOpcode::PHI), PHIVReg);245 for (auto BBRegPair : VRegs) {246 PHI.addReg(BBRegPair.second).addMBB(BBRegPair.first);247 }248 249 // We did not have a definition in this block before: store the phi's vreg250 // as this block downward exposed def.251 if (!UpwardsUse)252 setCurrentVReg(MBB, SwiftErrorVal, PHIVReg);253 }254 }255 256 // Create implicit defs for upward uses from unreachable blocks257 MachineRegisterInfo &MRI = MF->getRegInfo();258 for (const auto &Use : VRegUpwardsUse) {259 const MachineBasicBlock *UseBB = Use.first.first;260 Register VReg = Use.second;261 if (!MRI.def_empty(VReg))262 continue;263 264#ifdef EXPENSIVE_CHECKS265 assert(std::find(RPOT.begin(), RPOT.end(), UseBB) == RPOT.end() &&266 "Reachable block has VReg upward use without definition.");267#endif268 269 MachineBasicBlock *UseBBMut = MF->getBlockNumbered(UseBB->getNumber());270 271 BuildMI(*UseBBMut, UseBBMut->getFirstNonPHI(), DebugLoc(),272 TII->get(TargetOpcode::IMPLICIT_DEF), VReg);273 }274}275 276void SwiftErrorValueTracking::preassignVRegs(277 MachineBasicBlock *MBB, BasicBlock::const_iterator Begin,278 BasicBlock::const_iterator End) {279 if (!TLI->supportSwiftError() || SwiftErrorVals.empty())280 return;281 282 // Iterator over instructions and assign vregs to swifterror defs and uses.283 for (auto It = Begin; It != End; ++It) {284 if (auto *CB = dyn_cast<CallBase>(&*It)) {285 // A call-site with a swifterror argument is both use and def.286 const Value *SwiftErrorAddr = nullptr;287 for (const auto &Arg : CB->args()) {288 if (!Arg->isSwiftError())289 continue;290 // Use of swifterror.291 assert(!SwiftErrorAddr && "Cannot have multiple swifterror arguments");292 SwiftErrorAddr = &*Arg;293 assert(SwiftErrorAddr->isSwiftError() &&294 "Must have a swifterror value argument");295 getOrCreateVRegUseAt(&*It, MBB, SwiftErrorAddr);296 }297 if (!SwiftErrorAddr)298 continue;299 300 // Def of swifterror.301 getOrCreateVRegDefAt(&*It, MBB, SwiftErrorAddr);302 303 // A load is a use.304 } else if (const LoadInst *LI = dyn_cast<const LoadInst>(&*It)) {305 const Value *V = LI->getOperand(0);306 if (!V->isSwiftError())307 continue;308 309 getOrCreateVRegUseAt(LI, MBB, V);310 311 // A store is a def.312 } else if (const StoreInst *SI = dyn_cast<const StoreInst>(&*It)) {313 const Value *SwiftErrorAddr = SI->getOperand(1);314 if (!SwiftErrorAddr->isSwiftError())315 continue;316 317 // Def of swifterror.318 getOrCreateVRegDefAt(&*It, MBB, SwiftErrorAddr);319 320 // A return in a swiferror returning function is a use.321 } else if (const ReturnInst *R = dyn_cast<const ReturnInst>(&*It)) {322 const Function *F = R->getParent()->getParent();323 if (!F->getAttributes().hasAttrSomewhere(Attribute::SwiftError))324 continue;325 326 getOrCreateVRegUseAt(R, MBB, SwiftErrorArg);327 }328 }329}330