Files
swift-mirror/lib/IRGen/GenControl.cpp
Chris Lattner 1c3dc82449 Now that Joe moved ObjC cleanup emission up to SILGen, start zapping cleanups.
First step is some of the emission deatils.


Swift SVN r4836
2013-04-21 04:18:54 +00:00

463 lines
16 KiB
C++

//===--- GenControl.cpp - IR Generation for Control Flow ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements general IR generation for control flow.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/CallSite.h"
#include "llvm/IR/Function.h"
#include "Cleanup.h"
#include "IRGenFunction.h"
#include "IRGenModule.h"
using namespace swift;
using namespace irgen;
/// Create an alloca at the top of the function whose purpose is to
/// support the code in some way.
llvm::AllocaInst *
IRGenFunction::createSupportAlloca(llvm::Type *ty, Alignment align,
const llvm::Twine &name) {
llvm::AllocaInst *alloca = new llvm::AllocaInst(ty, name, AllocaIP);
alloca->setAlignment(align.getValue());
return alloca;
}
/// Like emitBlock, but merge the target block into its unique
/// predecessor if possible.
void IRBuilder::emitMergeableBlock(llvm::BasicBlock *BB) {
assert(ClearedIP == nullptr);
// Check our special case.
if (BB->hasOneUse()) {
llvm::BranchInst *br = dyn_cast<llvm::BranchInst>(*BB->use_begin());
if (br && br->isUnconditional()) {
IRBuilderBase::SetInsertPoint(br->getParent());
br->eraseFromParent();
delete BB;
return;
}
}
// Do the normal thing.
emitBlock(BB);
}
/// Insert the given basic block after the IP block and move the
/// insertion point to it. Only valid if the IP is valid.
void IRBuilder::emitBlock(llvm::BasicBlock *BB) {
assert(ClearedIP == nullptr);
llvm::BasicBlock *CurBB = GetInsertBlock();
assert(CurBB && "current insertion point is invalid");
CurBB->getParent()->getBasicBlockList().insertAfter(CurBB, BB);
IRBuilderBase::SetInsertPoint(BB);
}
/// Insert the given basic block "anywhere". The IP may be invalid,
/// in which case the block will be inserted after the block which
/// contained the IP before the IP was invalidated.
void IRBuilder::insertBlockAnywhere(llvm::BasicBlock *BB) {
llvm::BasicBlock *IP = GetInsertBlock();
if (!IP) {
assert(ClearedIP && "no insertion point and none saved, either");
IP = ClearedIP;
}
IP->getParent()->getBasicBlockList().insertAfter(IP, BB);
}
/// Insert the given basic block "anywhere" and move the insertion
/// point to it. The IP may be invalid, in which case the block will
/// be inserted after the block which contained the IP before the IP
/// was invalidated.
void IRBuilder::emitBlockAnywhere(llvm::BasicBlock *BB) {
insertBlockAnywhere(BB);
SetInsertPoint(BB);
}
/// Create a new basic block with the given name. The block is not
/// automatically inserted into the function.
llvm::BasicBlock *
IRGenFunction::createBasicBlock(const llvm::Twine &Name) {
return llvm::BasicBlock::Create(IGM.getLLVMContext(), Name);
}
/// Get or create the jump-destination variable.
llvm::Value *IRGenFunction::getJumpDestSlot() {
if (JumpDestSlot) return JumpDestSlot;
JumpDestSlot = new llvm::AllocaInst(IGM.Int32Ty, "jumpdest.var", AllocaIP);
return JumpDestSlot;
}
/// Get or create the unreachable block.
llvm::BasicBlock *IRGenFunction::getUnreachableBlock() {
if (UnreachableBB) return UnreachableBB;
// Create it at the very end of the function.
UnreachableBB = createBasicBlock("unreachable");
new llvm::UnreachableInst(UnreachableBB->getContext(), UnreachableBB);
CurFn->getBasicBlockList().push_back(UnreachableBB);
return UnreachableBB;
}
//****************************************************************************//
//******************************* EXCEPTIONS *********************************//
//****************************************************************************//
llvm::CallSite IRGenFunction::emitInvoke(llvm::CallingConv::ID convention,
llvm::Value *fn,
ArrayRef<llvm::Value*> args,
const llvm::AttributeSet &attrs) {
// TODO: exceptions!
llvm::CallInst *call = Builder.CreateCall(fn, args);
call->setAttributes(attrs);
call->setCallingConv(convention);
return call;
}
//****************************************************************************//
//******************************** CLEANUPS **********************************//
//****************************************************************************//
/// The control of a cleanup is the information dynamically recording
/// whether or not the cleanup is active.
///
/// The goal is to avoid creating and updating a flag variable
/// whenever possible.
///
/// The current design is that the control is either:
/// - the address of a flag variable which must be written
/// on every state change or
/// - a pair of stable IPs recording the range of instructions
/// covering the last state that the cleanup was in.
/// The control becomes a flag address as soon as a transition
/// is recorded which makes it clear that the cleanup has been
/// referenced while both active and inactive. Therefore there is
/// an invariant that at least one of these properties was false
/// at the last transition point of the cleanup.
class swift::irgen::CleanupControl {
llvm::AllocaInst *Flag;
IRBuilder::StableIP IPBegin;
IRBuilder::StableIP IPEnd;
public:
CleanupControl() : Flag(nullptr) {}
static CleanupControl forFlag(llvm::AllocaInst *flag) {
CleanupControl control;
control.Flag = flag;
return control;
}
static CleanupControl forIPRange(IRBuilder::StableIP begin,
IRBuilder::StableIP end) {
CleanupControl control;
control.Flag = nullptr;
control.IPBegin = begin;
control.IPEnd = end;
return control;
}
static CleanupControl forIP(IRBuilder::StableIP begin) {
return forIPRange(begin, begin);
}
bool hasFlag() const { return Flag != nullptr; }
llvm::AllocaInst *getFlag() const { assert(hasFlag()); return Flag; }
IRBuilder::StableIP getIPBegin() const {
assert(!hasFlag());
return IPBegin;
}
IRBuilder::StableIP getIPEnd() const {
assert(!hasFlag());
return IPEnd;
}
};
CleanupControl Cleanup::getControl() const {
if (HasControlFlag)
return CleanupControl::forFlag(
reinterpret_cast<llvm::AllocaInst*>(ControlBegin));
return CleanupControl::forIPRange(
IRBuilder::StableIP::getFromOpaqueValue(ControlBegin),
IRBuilder::StableIP::getFromOpaqueValue(ControlEnd));
}
void Cleanup::setControl(const CleanupControl &control) {
if (control.hasFlag()) {
HasControlFlag = true;
ControlBegin = control.getFlag();
} else {
HasControlFlag = false;
ControlBegin = control.getIPBegin().getOpaqueValue();
ControlEnd = control.getIPEnd().getOpaqueValue();
}
}
/// Alter a control flag at the current insertion point (which must be valid).
static void setFlagNow(IRGenFunction &IGF, llvm::AllocaInst *flag, bool value) {
assert(IGF.Builder.hasValidIP());
IGF.Builder.CreateStore(IGF.Builder.getInt1(value), flag, Alignment(1));
}
/// Alter a control flag at the given stable insertion point.
static void setFlagThen(IRGenFunction &IGF, IRBuilder::StableIP ip,
llvm::AllocaInst *flag, bool value) {
if (!ip.isValid()) {
assert(value == false && "activation point is unreachable!");
return;
}
llvm::StoreInst *store =
new llvm::StoreInst(IGF.Builder.getInt1(value), flag);
store->setAlignment(1);
ip.insert(store);
}
/// Create a control flag and set it on a cleanup.
static llvm::AllocaInst *createControlFlag(IRGenFunction &IGF,
Cleanup &cleanup) {
llvm::AllocaInst *flag =
IGF.createSupportAlloca(IGF.IGM.Int1Ty, Alignment(1), "cleanup.isactive");
cleanup.setControl(CleanupControl::forFlag(flag));
return flag;
}
/// Transition the given cleanup to using a flag for control.
static void transitionControlToFlag(IRGenFunction &IGF, Cleanup &cleanup) {
CleanupControl control = cleanup.getControl();
assert(!control.hasFlag());
bool isActiveNow = cleanup.isActive();
llvm::AllocaInst *flag = createControlFlag(IGF, cleanup);
setFlagThen(IGF, control.getIPBegin(), flag, isActiveNow);
setFlagThen(IGF, control.getIPEnd(), flag, !isActiveNow);
if (IGF.Builder.hasValidIP())
setFlagNow(IGF, flag, isActiveNow);
}
/// The top cleanup on the stack is dead. Pop it off and perform any
/// emission or forwarding necessary.
static void popAndEmitTopCleanup(IRGenFunction &IGF,
DiverseStackImpl<Cleanup> &stack) {
Cleanup &stackCleanup = *stack.begin();
assert(stackCleanup.isDead() && "popping a living cleanup");
if (!stackCleanup.isUsedWhileActive()) {
// emitBranch never directly branches to a cleanup that's currently
// inactive, and the popping/forwarding code never branches to a
// cleanup that's never been active.
assert(stackCleanup.getNormalEntryBlock() == nullptr);
// This is just the usual invariant about the control flag only
// existing on cleanups with both active and inactive references.
assert(!stackCleanup.getControl().hasFlag());
// Therefore we have nothing to do for this cleanup and can just
// pop it.
stack.pop();
return;
}
assert(0);
}
/// Remove all the dead cleanups on the top of the cleanup stack.
static void popAndEmitTopDeadCleanups(IRGenFunction &IGF,
DiverseStackImpl<Cleanup> &stack,
CleanupsDepth end) {
stack.checkIterator(end);
while (stack.stable_begin() != end && stack.begin()->isDead()) {
assert(!stack.empty());
// We might get better results popping them all at once.
popAndEmitTopCleanup(IGF, stack);
stack.checkIterator(end);
}
}
/// Leave a scope, with all its cleanups.
void IRGenFunction::endScope(CleanupsDepth depth) {
Cleanups.checkIterator(depth);
popAndEmitTopDeadCleanups(*this, Cleanups, InnermostScope);
}
/// End the scope induced by a single cleanup.
void IRGenFunction::endSingleCleanupScope() {
assert(!Cleanups.empty() && "popping empty stack!");
Cleanups.checkIterator(InnermostScope);
assert(Cleanups.stable_begin() != InnermostScope &&
"popping past innermost scope!");
endScope(Cleanups.stabilize(llvm::next(Cleanups.begin())));
}
/// Initialize a just-pushed cleanup.
Cleanup &IRGenFunction::initCleanup(Cleanup &cleanup, size_t allocSize,
CleanupState state) {
cleanup.AllocatedSize = allocSize;
cleanup.State = unsigned(state);
cleanup.UsedWhileActive = false;
cleanup.UsedWhileInactive = false;
cleanup.HasFallthroughOutflow = false;
// HasControlFlag set below
cleanup.NextDestLabel = 0;
cleanup.NormalEntryBB = nullptr;
// ControlBegin set below
// ControlEnd set below
cleanup.setControl(CleanupControl::forIP(Builder.getStableIP()));
return cleanup;
}
/// Change the state of a cleanup.
void IRGenFunction::setCleanupState(CleanupsDepth depth,
CleanupState newState) {
auto iter = Cleanups.find(depth);
assert(iter != Cleanups.end() && "changing state of end of stack");
setCleanupState(*iter, newState);
if (newState == CleanupState::Dead && iter == Cleanups.begin())
popAndEmitTopDeadCleanups(*this, Cleanups, InnermostScope);
}
void IRGenFunction::setCleanupState(Cleanup &cleanup, CleanupState newState) {
assert((newState != CleanupState::Active || Builder.hasValidIP()) &&
"activating cleanup at invalid IP");
// Do the transition now to avoid doing it in N places below.
CleanupState oldState = cleanup.getState();
cleanup.setState(newState);
assert(newState != oldState && "cleanup state is already active");
switch (oldState) {
case CleanupState::Dead:
llvm_unreachable("changing state of dead cleanup");
// We're either activating or killing off a dormant cleanup.
case CleanupState::Dormant:
switch (newState) {
case CleanupState::Dormant: llvm_unreachable("no transition");
// We're killing a dormant cleanup. This can probably happen.
// This isn't a state transition we need to do anything about,
// though.
case CleanupState::Dead:
return;
// We're activating a dormant cleanup.
case CleanupState::Active: {
CleanupControl control = cleanup.getControl();
// If we have a control flag already, just store to it.
if (control.hasFlag()) {
setFlagNow(*this, control.getFlag(), true);
return;
}
// Otherwise, the control is the IP range over which the cleanup
// was in an active state.
// If the cleanup has been referenced in both states, force it now.
if (cleanup.isUsedWhileActive() && cleanup.isUsedWhileInactive()) {
transitionControlToFlag(*this, cleanup);
return;
}
// If the cleanup was not referenced in this most recent dormant
// spurt, don't update the locations.
if (!cleanup.isUsedWhileInactive())
return;
// Otherwise, we have uses while inactive but none while active.
// Set the range to the range of the dormant period.
assert(!cleanup.isUsedWhileActive());
cleanup.setControl(CleanupControl::forIPRange(control.getIPEnd(),
Builder.getStableIP()));
return;
}
}
llvm_unreachable("bad cleanup state");
// We're deactivating an active cleanup, either temporarily or not.
// The code is the same either way.
case CleanupState::Active: {
CleanupControl control = cleanup.getControl();
// If we have a control flag already, just store to it.
if (control.hasFlag()) {
// Deactivation doesn't have to happen at a valid IP.
if (Builder.hasValidIP())
setFlagNow(*this, control.getFlag(), false);
return;
}
// Otherwise, the control is an IP range over which the cleanup
// was in a dormant state.
// If the cleanup has been referenced in both states, force it now.
if (cleanup.isUsedWhileActive() && cleanup.isUsedWhileInactive()) {
transitionControlToFlag(*this, cleanup);
return;
}
// If the cleanup was not referenced in this most recent active
// interval, don't update the locations.
if (cleanup.isUsedWhileActive()) {
// do nothing
// Otherwise, we have uses while active but none while inactive.
// Set the range to the range of the active period.
} else {
assert(!cleanup.isUsedWhileInactive());
cleanup.setControl(CleanupControl::forIPRange(control.getIPEnd(),
Builder.getStableIP()));
}
return;
}
}
llvm_unreachable("bad cleanup state");
}
/// Emit a branch to the given jump destination, threading out through
/// any cleanups we might need to run. Leaves the insertion point in
/// the current block.
void IRGenFunction::emitBranch(llvm::BasicBlock *cblock, CleanupsDepth cdepth) {
assert(Builder.hasValidIP());
auto depth = Cleanups.find(cdepth);
// Find the topmost active cleanup.
auto it = Cleanups.begin();
for (; it != depth; ++it) {
if (it->isActive())
break;
}
// If we got out to the destination depth, we're done.
assert(it == depth);
Builder.CreateBr(cblock);
}
// Anchor the Cleanup v-table in this translation unit.
void Cleanup::_anchor() {}