1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00
llvm-mirror/lib/CodeGen/GCRootLowering.cpp
Sander de Smalen e0eda3654e [SVE] Return StackOffset for TargetFrameLowering::getFrameIndexReference.
To accommodate frame layouts that have both fixed and scalable objects
on the stack, describing a stack location or offset using a pointer + uint64_t
is not sufficient. For this reason, we've introduced the StackOffset class,
which models both the fixed- and scalable sized offsets.

The TargetFrameLowering::getFrameIndexReference is made to return a StackOffset,
so that this can be used in other interfaces, such as to eliminate frame indices
in PEI or to emit Debug locations for variables on the stack.

This patch is purely mechanical and doesn't change the behaviour of how
the result of this function is used for fixed-sized offsets. The patch adds
various checks to assert that the offset has no scalable component, as frame
offsets with a scalable component are not yet supported in various places.

Reviewed By: arsenm

Differential Revision: https://reviews.llvm.org/D90018
2020-11-05 11:02:18 +00:00

333 lines
12 KiB
C++

//===-- GCRootLowering.cpp - Garbage collection infrastructure ------------===//
//
// 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 file implements the lowering for the gc.root mechanism.
//
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/GCMetadata.h"
#include "llvm/CodeGen/GCStrategy.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace {
/// LowerIntrinsics - This pass rewrites calls to the llvm.gcread or
/// llvm.gcwrite intrinsics, replacing them with simple loads and stores as
/// directed by the GCStrategy. It also performs automatic root initialization
/// and custom intrinsic lowering.
class LowerIntrinsics : public FunctionPass {
bool DoLowering(Function &F, GCStrategy &S);
public:
static char ID;
LowerIntrinsics();
StringRef getPassName() const override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool doInitialization(Module &M) override;
bool runOnFunction(Function &F) override;
};
/// GCMachineCodeAnalysis - This is a target-independent pass over the machine
/// function representation to identify safe points for the garbage collector
/// in the machine code. It inserts labels at safe points and populates a
/// GCMetadata record for each function.
class GCMachineCodeAnalysis : public MachineFunctionPass {
GCFunctionInfo *FI;
const TargetInstrInfo *TII;
void FindSafePoints(MachineFunction &MF);
void VisitCallPoint(MachineBasicBlock::iterator CI);
MCSymbol *InsertLabel(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
const DebugLoc &DL) const;
void FindStackOffsets(MachineFunction &MF);
public:
static char ID;
GCMachineCodeAnalysis();
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnMachineFunction(MachineFunction &MF) override;
};
}
// -----------------------------------------------------------------------------
INITIALIZE_PASS_BEGIN(LowerIntrinsics, "gc-lowering", "GC Lowering", false,
false)
INITIALIZE_PASS_DEPENDENCY(GCModuleInfo)
INITIALIZE_PASS_END(LowerIntrinsics, "gc-lowering", "GC Lowering", false, false)
FunctionPass *llvm::createGCLoweringPass() { return new LowerIntrinsics(); }
char LowerIntrinsics::ID = 0;
LowerIntrinsics::LowerIntrinsics() : FunctionPass(ID) {
initializeLowerIntrinsicsPass(*PassRegistry::getPassRegistry());
}
StringRef LowerIntrinsics::getPassName() const {
return "Lower Garbage Collection Instructions";
}
void LowerIntrinsics::getAnalysisUsage(AnalysisUsage &AU) const {
FunctionPass::getAnalysisUsage(AU);
AU.addRequired<GCModuleInfo>();
AU.addPreserved<DominatorTreeWrapperPass>();
}
/// doInitialization - If this module uses the GC intrinsics, find them now.
bool LowerIntrinsics::doInitialization(Module &M) {
GCModuleInfo *MI = getAnalysisIfAvailable<GCModuleInfo>();
assert(MI && "LowerIntrinsics didn't require GCModuleInfo!?");
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
if (!I->isDeclaration() && I->hasGC())
MI->getFunctionInfo(*I); // Instantiate the GC strategy.
return false;
}
/// CouldBecomeSafePoint - Predicate to conservatively determine whether the
/// instruction could introduce a safe point.
static bool CouldBecomeSafePoint(Instruction *I) {
// The natural definition of instructions which could introduce safe points
// are:
//
// - call, invoke (AfterCall, BeforeCall)
// - phis (Loops)
// - invoke, ret, unwind (Exit)
//
// However, instructions as seemingly inoccuous as arithmetic can become
// libcalls upon lowering (e.g., div i64 on a 32-bit platform), so instead
// it is necessary to take a conservative approach.
if (isa<AllocaInst>(I) || isa<GetElementPtrInst>(I) || isa<StoreInst>(I) ||
isa<LoadInst>(I))
return false;
// llvm.gcroot is safe because it doesn't do anything at runtime.
if (CallInst *CI = dyn_cast<CallInst>(I))
if (Function *F = CI->getCalledFunction())
if (Intrinsic::ID IID = F->getIntrinsicID())
if (IID == Intrinsic::gcroot)
return false;
return true;
}
static bool InsertRootInitializers(Function &F, ArrayRef<AllocaInst *> Roots) {
// Scroll past alloca instructions.
BasicBlock::iterator IP = F.getEntryBlock().begin();
while (isa<AllocaInst>(IP))
++IP;
// Search for initializers in the initial BB.
SmallPtrSet<AllocaInst *, 16> InitedRoots;
for (; !CouldBecomeSafePoint(&*IP); ++IP)
if (StoreInst *SI = dyn_cast<StoreInst>(IP))
if (AllocaInst *AI =
dyn_cast<AllocaInst>(SI->getOperand(1)->stripPointerCasts()))
InitedRoots.insert(AI);
// Add root initializers.
bool MadeChange = false;
for (AllocaInst *Root : Roots)
if (!InitedRoots.count(Root)) {
new StoreInst(
ConstantPointerNull::get(cast<PointerType>(Root->getAllocatedType())),
Root, Root->getNextNode());
MadeChange = true;
}
return MadeChange;
}
/// runOnFunction - Replace gcread/gcwrite intrinsics with loads and stores.
/// Leave gcroot intrinsics; the code generator needs to see those.
bool LowerIntrinsics::runOnFunction(Function &F) {
// Quick exit for functions that do not use GC.
if (!F.hasGC())
return false;
GCFunctionInfo &FI = getAnalysis<GCModuleInfo>().getFunctionInfo(F);
GCStrategy &S = FI.getStrategy();
return DoLowering(F, S);
}
/// Lower barriers out of existance (if the associated GCStrategy hasn't
/// already done so...), and insert initializing stores to roots as a defensive
/// measure. Given we're going to report all roots live at all safepoints, we
/// need to be able to ensure each root has been initialized by the point the
/// first safepoint is reached. This really should have been done by the
/// frontend, but the old API made this non-obvious, so we do a potentially
/// redundant store just in case.
bool LowerIntrinsics::DoLowering(Function &F, GCStrategy &S) {
SmallVector<AllocaInst *, 32> Roots;
bool MadeChange = false;
for (BasicBlock &BB : F)
for (BasicBlock::iterator II = BB.begin(), E = BB.end(); II != E;) {
IntrinsicInst *CI = dyn_cast<IntrinsicInst>(II++);
if (!CI)
continue;
Function *F = CI->getCalledFunction();
switch (F->getIntrinsicID()) {
default: break;
case Intrinsic::gcwrite: {
// Replace a write barrier with a simple store.
Value *St = new StoreInst(CI->getArgOperand(0),
CI->getArgOperand(2), CI);
CI->replaceAllUsesWith(St);
CI->eraseFromParent();
MadeChange = true;
break;
}
case Intrinsic::gcread: {
// Replace a read barrier with a simple load.
Value *Ld = new LoadInst(CI->getType(), CI->getArgOperand(1), "", CI);
Ld->takeName(CI);
CI->replaceAllUsesWith(Ld);
CI->eraseFromParent();
MadeChange = true;
break;
}
case Intrinsic::gcroot: {
// Initialize the GC root, but do not delete the intrinsic. The
// backend needs the intrinsic to flag the stack slot.
Roots.push_back(
cast<AllocaInst>(CI->getArgOperand(0)->stripPointerCasts()));
break;
}
}
}
if (Roots.size())
MadeChange |= InsertRootInitializers(F, Roots);
return MadeChange;
}
// -----------------------------------------------------------------------------
char GCMachineCodeAnalysis::ID = 0;
char &llvm::GCMachineCodeAnalysisID = GCMachineCodeAnalysis::ID;
INITIALIZE_PASS(GCMachineCodeAnalysis, "gc-analysis",
"Analyze Machine Code For Garbage Collection", false, false)
GCMachineCodeAnalysis::GCMachineCodeAnalysis() : MachineFunctionPass(ID) {}
void GCMachineCodeAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {
MachineFunctionPass::getAnalysisUsage(AU);
AU.setPreservesAll();
AU.addRequired<GCModuleInfo>();
}
MCSymbol *GCMachineCodeAnalysis::InsertLabel(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
const DebugLoc &DL) const {
MCSymbol *Label = MBB.getParent()->getContext().createTempSymbol();
BuildMI(MBB, MI, DL, TII->get(TargetOpcode::GC_LABEL)).addSym(Label);
return Label;
}
void GCMachineCodeAnalysis::VisitCallPoint(MachineBasicBlock::iterator CI) {
// Find the return address (next instruction), since that's what will be on
// the stack when the call is suspended and we need to inspect the stack.
MachineBasicBlock::iterator RAI = CI;
++RAI;
MCSymbol *Label = InsertLabel(*CI->getParent(), RAI, CI->getDebugLoc());
FI->addSafePoint(Label, CI->getDebugLoc());
}
void GCMachineCodeAnalysis::FindSafePoints(MachineFunction &MF) {
for (MachineBasicBlock &MBB : MF)
for (MachineBasicBlock::iterator MI = MBB.begin(), ME = MBB.end();
MI != ME; ++MI)
if (MI->isCall()) {
// Do not treat tail or sibling call sites as safe points. This is
// legal since any arguments passed to the callee which live in the
// remnants of the callers frame will be owned and updated by the
// callee if required.
if (MI->isTerminator())
continue;
VisitCallPoint(MI);
}
}
void GCMachineCodeAnalysis::FindStackOffsets(MachineFunction &MF) {
const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
assert(TFI && "TargetRegisterInfo not available!");
for (GCFunctionInfo::roots_iterator RI = FI->roots_begin();
RI != FI->roots_end();) {
// If the root references a dead object, no need to keep it.
if (MF.getFrameInfo().isDeadObjectIndex(RI->Num)) {
RI = FI->removeStackRoot(RI);
} else {
Register FrameReg; // FIXME: surely GCRoot ought to store the
// register that the offset is from?
auto FrameOffset = TFI->getFrameIndexReference(MF, RI->Num, FrameReg);
assert(!FrameOffset.getScalable() &&
"Frame offsets with a scalable component are not supported");
RI->StackOffset = FrameOffset.getFixed();
++RI;
}
}
}
bool GCMachineCodeAnalysis::runOnMachineFunction(MachineFunction &MF) {
// Quick exit for functions that do not use GC.
if (!MF.getFunction().hasGC())
return false;
FI = &getAnalysis<GCModuleInfo>().getFunctionInfo(MF.getFunction());
TII = MF.getSubtarget().getInstrInfo();
// Find the size of the stack frame. There may be no correct static frame
// size, we use UINT64_MAX to represent this.
const MachineFrameInfo &MFI = MF.getFrameInfo();
const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo();
const bool DynamicFrameSize = MFI.hasVarSizedObjects() ||
RegInfo->needsStackRealignment(MF);
FI->setFrameSize(DynamicFrameSize ? UINT64_MAX : MFI.getStackSize());
// Find all safe points.
if (FI->getStrategy().needsSafePoints())
FindSafePoints(MF);
// Find the concrete stack offsets for all roots (stack slots)
FindStackOffsets(MF);
return false;
}