forked from OSchip/llvm-project
313 lines
11 KiB
C++
313 lines
11 KiB
C++
//===-- SwiftErrorValueTracking.cpp --------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This implements a limited mem2reg-like analysis to promote uses of function
|
|
// arguments and allocas marked with swiftalloc from memory into virtual
|
|
// registers tracked by this class.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/CodeGen/SwiftErrorValueTracking.h"
|
|
#include "llvm/ADT/SmallSet.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/TargetInstrInfo.h"
|
|
#include "llvm/CodeGen/TargetLowering.h"
|
|
#include "llvm/IR/Value.h"
|
|
|
|
using namespace llvm;
|
|
|
|
unsigned SwiftErrorValueTracking::getOrCreateVReg(const MachineBasicBlock *MBB,
|
|
const Value *Val) {
|
|
auto Key = std::make_pair(MBB, Val);
|
|
auto It = VRegDefMap.find(Key);
|
|
// If this is the first use of this swifterror value in this basic block,
|
|
// create a new virtual register.
|
|
// After we processed all basic blocks we will satisfy this "upwards exposed
|
|
// use" by inserting a copy or phi at the beginning of this block.
|
|
if (It == VRegDefMap.end()) {
|
|
auto &DL = MF->getDataLayout();
|
|
const TargetRegisterClass *RC = TLI->getRegClassFor(TLI->getPointerTy(DL));
|
|
auto VReg = MF->getRegInfo().createVirtualRegister(RC);
|
|
VRegDefMap[Key] = VReg;
|
|
VRegUpwardsUse[Key] = VReg;
|
|
return VReg;
|
|
} else
|
|
return It->second;
|
|
}
|
|
|
|
void SwiftErrorValueTracking::setCurrentVReg(const MachineBasicBlock *MBB,
|
|
const Value *Val, unsigned VReg) {
|
|
VRegDefMap[std::make_pair(MBB, Val)] = VReg;
|
|
}
|
|
|
|
unsigned SwiftErrorValueTracking::getOrCreateVRegDefAt(
|
|
const Instruction *I, const MachineBasicBlock *MBB, const Value *Val) {
|
|
auto Key = PointerIntPair<const Instruction *, 1, bool>(I, true);
|
|
auto It = VRegDefUses.find(Key);
|
|
if (It != VRegDefUses.end())
|
|
return It->second;
|
|
|
|
auto &DL = MF->getDataLayout();
|
|
const TargetRegisterClass *RC = TLI->getRegClassFor(TLI->getPointerTy(DL));
|
|
unsigned VReg = MF->getRegInfo().createVirtualRegister(RC);
|
|
VRegDefUses[Key] = VReg;
|
|
setCurrentVReg(MBB, Val, VReg);
|
|
return VReg;
|
|
}
|
|
|
|
unsigned SwiftErrorValueTracking::getOrCreateVRegUseAt(
|
|
const Instruction *I, const MachineBasicBlock *MBB, const Value *Val) {
|
|
auto Key = PointerIntPair<const Instruction *, 1, bool>(I, false);
|
|
auto It = VRegDefUses.find(Key);
|
|
if (It != VRegDefUses.end())
|
|
return It->second;
|
|
|
|
unsigned VReg = getOrCreateVReg(MBB, Val);
|
|
VRegDefUses[Key] = VReg;
|
|
return VReg;
|
|
}
|
|
|
|
/// Set up SwiftErrorVals by going through the function. If the function has
|
|
/// swifterror argument, it will be the first entry.
|
|
void SwiftErrorValueTracking::setFunction(MachineFunction &mf) {
|
|
MF = &mf;
|
|
Fn = &MF->getFunction();
|
|
TLI = MF->getSubtarget().getTargetLowering();
|
|
TII = MF->getSubtarget().getInstrInfo();
|
|
|
|
if (!TLI->supportSwiftError())
|
|
return;
|
|
|
|
SwiftErrorVals.clear();
|
|
VRegDefMap.clear();
|
|
VRegUpwardsUse.clear();
|
|
VRegDefUses.clear();
|
|
SwiftErrorArg = nullptr;
|
|
|
|
// Check if function has a swifterror argument.
|
|
bool HaveSeenSwiftErrorArg = false;
|
|
for (Function::const_arg_iterator AI = Fn->arg_begin(), AE = Fn->arg_end();
|
|
AI != AE; ++AI)
|
|
if (AI->hasSwiftErrorAttr()) {
|
|
assert(!HaveSeenSwiftErrorArg &&
|
|
"Must have only one swifterror parameter");
|
|
(void)HaveSeenSwiftErrorArg; // silence warning.
|
|
HaveSeenSwiftErrorArg = true;
|
|
SwiftErrorArg = &*AI;
|
|
SwiftErrorVals.push_back(&*AI);
|
|
}
|
|
|
|
for (const auto &LLVMBB : *Fn)
|
|
for (const auto &Inst : LLVMBB) {
|
|
if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(&Inst))
|
|
if (Alloca->isSwiftError())
|
|
SwiftErrorVals.push_back(Alloca);
|
|
}
|
|
}
|
|
|
|
bool SwiftErrorValueTracking::createEntriesInEntryBlock(DebugLoc DbgLoc) {
|
|
if (!TLI->supportSwiftError())
|
|
return false;
|
|
|
|
// We only need to do this when we have swifterror parameter or swifterror
|
|
// alloc.
|
|
if (SwiftErrorVals.empty())
|
|
return false;
|
|
|
|
MachineBasicBlock *MBB = &*MF->begin();
|
|
auto &DL = MF->getDataLayout();
|
|
auto const *RC = TLI->getRegClassFor(TLI->getPointerTy(DL));
|
|
bool Inserted = false;
|
|
for (const auto *SwiftErrorVal : SwiftErrorVals) {
|
|
// We will always generate a copy from the argument. It is always used at
|
|
// least by the 'return' of the swifterror.
|
|
if (SwiftErrorArg && SwiftErrorArg == SwiftErrorVal)
|
|
continue;
|
|
unsigned VReg = MF->getRegInfo().createVirtualRegister(RC);
|
|
// Assign Undef to Vreg. We construct MI directly to make sure it works
|
|
// with FastISel.
|
|
BuildMI(*MBB, MBB->getFirstNonPHI(), DbgLoc,
|
|
TII->get(TargetOpcode::IMPLICIT_DEF), VReg);
|
|
|
|
setCurrentVReg(MBB, SwiftErrorVal, VReg);
|
|
Inserted = true;
|
|
}
|
|
|
|
return Inserted;
|
|
}
|
|
|
|
/// Propagate swifterror values through the machine function CFG.
|
|
void SwiftErrorValueTracking::propagateVRegs() {
|
|
if (!TLI->supportSwiftError())
|
|
return;
|
|
|
|
// We only need to do this when we have swifterror parameter or swifterror
|
|
// alloc.
|
|
if (SwiftErrorVals.empty())
|
|
return;
|
|
|
|
// For each machine basic block in reverse post order.
|
|
ReversePostOrderTraversal<MachineFunction *> RPOT(MF);
|
|
for (MachineBasicBlock *MBB : RPOT) {
|
|
// For each swifterror value in the function.
|
|
for (const auto *SwiftErrorVal : SwiftErrorVals) {
|
|
auto Key = std::make_pair(MBB, SwiftErrorVal);
|
|
auto UUseIt = VRegUpwardsUse.find(Key);
|
|
auto VRegDefIt = VRegDefMap.find(Key);
|
|
bool UpwardsUse = UUseIt != VRegUpwardsUse.end();
|
|
unsigned UUseVReg = UpwardsUse ? UUseIt->second : 0;
|
|
bool DownwardDef = VRegDefIt != VRegDefMap.end();
|
|
assert(!(UpwardsUse && !DownwardDef) &&
|
|
"We can't have an upwards use but no downwards def");
|
|
|
|
// If there is no upwards exposed use and an entry for the swifterror in
|
|
// the def map for this value we don't need to do anything: We already
|
|
// have a downward def for this basic block.
|
|
if (!UpwardsUse && DownwardDef)
|
|
continue;
|
|
|
|
// Otherwise we either have an upwards exposed use vreg that we need to
|
|
// materialize or need to forward the downward def from predecessors.
|
|
|
|
// Check whether we have a single vreg def from all predecessors.
|
|
// Otherwise we need a phi.
|
|
SmallVector<std::pair<MachineBasicBlock *, unsigned>, 4> VRegs;
|
|
SmallSet<const MachineBasicBlock *, 8> Visited;
|
|
for (auto *Pred : MBB->predecessors()) {
|
|
if (!Visited.insert(Pred).second)
|
|
continue;
|
|
VRegs.push_back(std::make_pair(
|
|
Pred, getOrCreateVReg(Pred, SwiftErrorVal)));
|
|
if (Pred != MBB)
|
|
continue;
|
|
// We have a self-edge.
|
|
// If there was no upwards use in this basic block there is now one: the
|
|
// phi needs to use it self.
|
|
if (!UpwardsUse) {
|
|
UpwardsUse = true;
|
|
UUseIt = VRegUpwardsUse.find(Key);
|
|
assert(UUseIt != VRegUpwardsUse.end());
|
|
UUseVReg = UUseIt->second;
|
|
}
|
|
}
|
|
|
|
// We need a phi node if we have more than one predecessor with different
|
|
// downward defs.
|
|
bool needPHI =
|
|
VRegs.size() >= 1 &&
|
|
std::find_if(
|
|
VRegs.begin(), VRegs.end(),
|
|
[&](const std::pair<const MachineBasicBlock *, unsigned> &V)
|
|
-> bool { return V.second != VRegs[0].second; }) !=
|
|
VRegs.end();
|
|
|
|
// If there is no upwards exposed used and we don't need a phi just
|
|
// forward the swifterror vreg from the predecessor(s).
|
|
if (!UpwardsUse && !needPHI) {
|
|
assert(!VRegs.empty() &&
|
|
"No predecessors? The entry block should bail out earlier");
|
|
// Just forward the swifterror vreg from the predecessor(s).
|
|
setCurrentVReg(MBB, SwiftErrorVal, VRegs[0].second);
|
|
continue;
|
|
}
|
|
|
|
auto DLoc = isa<Instruction>(SwiftErrorVal)
|
|
? cast<Instruction>(SwiftErrorVal)->getDebugLoc()
|
|
: DebugLoc();
|
|
const auto *TII = MF->getSubtarget().getInstrInfo();
|
|
|
|
// If we don't need a phi create a copy to the upward exposed vreg.
|
|
if (!needPHI) {
|
|
assert(UpwardsUse);
|
|
assert(!VRegs.empty() &&
|
|
"No predecessors? Is the Calling Convention correct?");
|
|
unsigned DestReg = UUseVReg;
|
|
BuildMI(*MBB, MBB->getFirstNonPHI(), DLoc, TII->get(TargetOpcode::COPY),
|
|
DestReg)
|
|
.addReg(VRegs[0].second);
|
|
continue;
|
|
}
|
|
|
|
// We need a phi: if there is an upwards exposed use we already have a
|
|
// destination virtual register number otherwise we generate a new one.
|
|
auto &DL = MF->getDataLayout();
|
|
auto const *RC = TLI->getRegClassFor(TLI->getPointerTy(DL));
|
|
unsigned PHIVReg =
|
|
UpwardsUse ? UUseVReg : MF->getRegInfo().createVirtualRegister(RC);
|
|
MachineInstrBuilder PHI =
|
|
BuildMI(*MBB, MBB->getFirstNonPHI(), DLoc,
|
|
TII->get(TargetOpcode::PHI), PHIVReg);
|
|
for (auto BBRegPair : VRegs) {
|
|
PHI.addReg(BBRegPair.second).addMBB(BBRegPair.first);
|
|
}
|
|
|
|
// We did not have a definition in this block before: store the phi's vreg
|
|
// as this block downward exposed def.
|
|
if (!UpwardsUse)
|
|
setCurrentVReg(MBB, SwiftErrorVal, PHIVReg);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SwiftErrorValueTracking::preassignVRegs(
|
|
MachineBasicBlock *MBB, BasicBlock::const_iterator Begin,
|
|
BasicBlock::const_iterator End) {
|
|
if (!TLI->supportSwiftError() || SwiftErrorVals.empty())
|
|
return;
|
|
|
|
// Iterator over instructions and assign vregs to swifterror defs and uses.
|
|
for (auto It = Begin; It != End; ++It) {
|
|
ImmutableCallSite CS(&*It);
|
|
if (CS) {
|
|
// A call-site with a swifterror argument is both use and def.
|
|
const Value *SwiftErrorAddr = nullptr;
|
|
for (auto &Arg : CS.args()) {
|
|
if (!Arg->isSwiftError())
|
|
continue;
|
|
// Use of swifterror.
|
|
assert(!SwiftErrorAddr && "Cannot have multiple swifterror arguments");
|
|
SwiftErrorAddr = &*Arg;
|
|
assert(SwiftErrorAddr->isSwiftError() &&
|
|
"Must have a swifterror value argument");
|
|
getOrCreateVRegUseAt(&*It, MBB, SwiftErrorAddr);
|
|
}
|
|
if (!SwiftErrorAddr)
|
|
continue;
|
|
|
|
// Def of swifterror.
|
|
getOrCreateVRegDefAt(&*It, MBB, SwiftErrorAddr);
|
|
|
|
// A load is a use.
|
|
} else if (const LoadInst *LI = dyn_cast<const LoadInst>(&*It)) {
|
|
const Value *V = LI->getOperand(0);
|
|
if (!V->isSwiftError())
|
|
continue;
|
|
|
|
getOrCreateVRegUseAt(LI, MBB, V);
|
|
|
|
// A store is a def.
|
|
} else if (const StoreInst *SI = dyn_cast<const StoreInst>(&*It)) {
|
|
const Value *SwiftErrorAddr = SI->getOperand(1);
|
|
if (!SwiftErrorAddr->isSwiftError())
|
|
continue;
|
|
|
|
// Def of swifterror.
|
|
getOrCreateVRegDefAt(&*It, MBB, SwiftErrorAddr);
|
|
|
|
// A return in a swiferror returning function is a use.
|
|
} else if (const ReturnInst *R = dyn_cast<const ReturnInst>(&*It)) {
|
|
const Function *F = R->getParent()->getParent();
|
|
if (!F->getAttributes().hasAttrSomewhere(Attribute::SwiftError))
|
|
continue;
|
|
|
|
getOrCreateVRegUseAt(R, MBB, SwiftErrorArg);
|
|
}
|
|
}
|
|
}
|