IRGen for break/continue.

Swift SVN r2089
This commit is contained in:
Eli Friedman
2012-05-31 02:45:18 +00:00
parent c404598fcb
commit 26bebcfd61
13 changed files with 234 additions and 49 deletions

View File

@@ -21,7 +21,6 @@
namespace swift {
namespace irgen {
class IRGenFunction;
// These classes are private to GenCleanup.cpp.
class CleanupControl;

View File

@@ -33,13 +33,13 @@ namespace irgen {
/// cleanup.
class ManagedValue {
llvm::Value *Value;
IRGenFunction::CleanupsDepth Cleanup;
CleanupsDepth Cleanup;
public:
ManagedValue() = default;
explicit ManagedValue(llvm::Value *value)
: Value(value), Cleanup(IRGenFunction::CleanupsDepth::invalid()) {}
ManagedValue(llvm::Value *value, IRGenFunction::CleanupsDepth cleanup)
: Value(value), Cleanup(CleanupsDepth::invalid()) {}
ManagedValue(llvm::Value *value, CleanupsDepth cleanup)
: Value(value), Cleanup(cleanup) {}
llvm::Value *getUnmanagedValue() const {
@@ -49,7 +49,7 @@ public:
llvm::Value *getValue() const { return Value; }
bool hasCleanup() const { return Cleanup.isValid(); }
IRGenFunction::CleanupsDepth getCleanup() const { return Cleanup; }
CleanupsDepth getCleanup() const { return Cleanup; }
/// Forward this value, deactivating the cleanup and returning the
/// underlying value.
@@ -60,8 +60,7 @@ public:
}
/// Split this value into its underlying value and, if present, its cleanup.
llvm::Value *split(llvm::SmallVectorImpl<IRGenFunction::CleanupsDepth>
&cleanups) {
llvm::Value *split(llvm::SmallVectorImpl<CleanupsDepth> &cleanups) {
if (hasCleanup()) cleanups.push_back(getCleanup());
return getValue();
}

View File

@@ -663,7 +663,7 @@ static void popAndEmitTopCleanup(IRGenFunction &IGF,
/// Remove all the dead cleanups on the top of the cleanup stack.
static void popAndEmitTopDeadCleanups(IRGenFunction &IGF,
DiverseStackImpl<Cleanup> &stack,
IRGenFunction::CleanupsDepth end) {
CleanupsDepth end) {
stack.checkIterator(end);
while (stack.stable_begin() != end && stack.begin()->isDead()) {

View File

@@ -1040,7 +1040,7 @@ namespace {
unsigned LastArgWritten;
SmallVector<llvm::Value*, 16> Args;
SmallVector<IRGenFunction::CleanupsDepth, 16> ArgCleanups;
SmallVector<CleanupsDepth, 16> ArgCleanups;
protected:
CallEmitter(IRGenFunction &IGF, const Callee &callee,

View File

@@ -591,7 +591,7 @@ namespace {
/// Enter a cleanup to call swift_dealloc on the given pointer.
/// This cleanup will usually be deactivated as soon as the
/// initializer completes.
IRGenFunction::CleanupsDepth
CleanupsDepth
IRGenFunction::pushDeallocCleanup(llvm::Value *allocation,
llvm::Value *size) {
pushFullExprCleanup<CallDealloc>(allocation, size);

View File

@@ -70,7 +70,7 @@ static OnHeap_t isOnHeap(VarDecl *var) {
void Initialization::registerObject(IRGenFunction &IGF, Object object,
OnHeap_t onHeap, const TypeInfo &objectTI) {
// Create the appropriate destroy cleanup.
IRGenFunction::CleanupsDepth destroy;
CleanupsDepth destroy;
// We need a destroy cleanup if the object is on the heap or non-POD.
if (onHeap || !objectTI.isPOD(ResilienceScope::Local)) {
@@ -78,24 +78,24 @@ void Initialization::registerObject(IRGenFunction &IGF, Object object,
objectTI);
destroy = IGF.getCleanupsDepth();
} else {
destroy = IRGenFunction::CleanupsDepth::invalid();
destroy = CleanupsDepth::invalid();
}
registerObject(object, destroy);
}
void Initialization::registerObjectWithoutDestroy(Object object) {
registerObject(object, IRGenFunction::CleanupsDepth::invalid());
registerObject(object, CleanupsDepth::invalid());
}
/// Register an object with the initialization process.
void Initialization::registerObject(Object object,
IRGenFunction::CleanupsDepth destroy) {
CleanupsDepth destroy) {
// The invariant is that the cleanup has to be an
// UnboundDestroy if it's valid.
ValueRecord record = {
IRGenFunction::CleanupsDepth::invalid(), destroy
CleanupsDepth::invalid(), destroy
};
Records.insert(std::make_pair(object.Opaque, record));
}
@@ -103,7 +103,7 @@ void Initialization::registerObject(Object object,
/// Mark that an object has been allocated.
void Initialization::markAllocated(IRGenFunction &IGF, Object object,
OwnedAddress address,
IRGenFunction::CleanupsDepth dealloc) {
CleanupsDepth dealloc) {
ValueRecord &record = Records.find(object.Opaque)->second;
record.DeallocCleanup = dealloc;
@@ -161,7 +161,7 @@ Initialization::emitLocalAllocation(IRGenFunction &IGF, Object object,
// If the type is known to be empty, don't actually allocate anything.
if (type.isEmpty(ResilienceScope::Local)) {
OwnedAddress addr = createEmptyAlloca(IGF.IGM, type);
markAllocated(IGF, object, addr, IRGenFunction::CleanupsDepth::invalid());
markAllocated(IGF, object, addr, CleanupsDepth::invalid());
return addr;
}
@@ -173,7 +173,7 @@ Initialization::emitLocalAllocation(IRGenFunction &IGF, Object object,
// TODO: lifetime intrinsics?
OwnedAddress addr(rawAddr, IGF.IGM.RefCountedNull);
markAllocated(IGF, object, addr, IRGenFunction::CleanupsDepth::invalid());
markAllocated(IGF, object, addr, CleanupsDepth::invalid());
return addr;
}
@@ -192,7 +192,7 @@ Initialization::emitLocalAllocation(IRGenFunction &IGF, Object object,
// Push a cleanup to dealloc the allocation.
// FIXME: don't emit the size twice!
IRGenFunction::CleanupsDepth deallocCleanup
CleanupsDepth deallocCleanup
= IGF.pushDeallocCleanup(allocation, layout.emitSize(IGF));
OwnedAddress addr(rawAddr, allocation);
@@ -201,7 +201,7 @@ Initialization::emitLocalAllocation(IRGenFunction &IGF, Object object,
}
static void maybeSetCleanupState(IRGenFunction &IGF,
IRGenFunction::CleanupsDepth maybeCleanup,
CleanupsDepth maybeCleanup,
CleanupState newState) {
if (maybeCleanup.isValid())
IGF.setCleanupState(maybeCleanup, newState);

View File

@@ -46,8 +46,8 @@ namespace irgen {
/// variable initialization.
class Initialization {
struct ValueRecord {
IRGenFunction::CleanupsDepth DeallocCleanup;
IRGenFunction::CleanupsDepth DestroyCleanup;
CleanupsDepth DeallocCleanup;
CleanupsDepth DestroyCleanup;
};
llvm::DenseMap<void *, ValueRecord> Records;
@@ -108,14 +108,14 @@ public:
/// trivial and there isn't a dealloc cleanup.
void markAllocated(IRGenFunction &IGF, Object object,
OwnedAddress address,
IRGenFunction::CleanupsDepth dealloc);
CleanupsDepth dealloc);
/// Mark that the value has reached its instant of initialization.
void markInitialized(IRGenFunction &IGF, Object object);
private:
/// Add an object that is going to be initialized.
void registerObject(Object object, IRGenFunction::CleanupsDepth destroy);
void registerObject(Object object, CleanupsDepth destroy);
};
} // end namespace irgen

View File

@@ -1828,8 +1828,7 @@ namespace {
// Pull off the outer result.
FixedPacking innerResultPacking;
IRGenFunction::CleanupsDepth outerResultCleanup
= IRGenFunction::CleanupsDepth::invalid();
CleanupsDepth outerResultCleanup = CleanupsDepth::invalid();
if (HasAbstractedResult) {
outerResult = Address(outerArgs.claimUnmanagedNext(),
getFixedBufferAlignment(IGM));
@@ -2182,7 +2181,7 @@ void irgen::emitErasureAsInit(IRGenFunction &IGF, ErasureExpr *E,
Address object = emitAllocateBuffer(IGF, buffer, packing, concreteTI);
// Push a cleanup to destroy that.
IRGenFunction::CleanupsDepth deallocCleanup;
CleanupsDepth deallocCleanup;
bool needsDeallocCleanup = !isNeverAllocated(packing);
if (needsDeallocCleanup) {
IGF.pushFullExprCleanup<DeallocateBuffer>(buffer, packing, concreteTI);

View File

@@ -57,12 +57,10 @@ void IRGenFunction::emitStmt(Stmt *S) {
return emitForEachStmt(cast<ForEachStmt>(S));
case StmtKind::Break:
unimplemented(cast<BreakStmt>(S)->getLoc(), "break statement");
return;
return emitBreakStmt(cast<BreakStmt>(S));
case StmtKind::Continue:
unimplemented(cast<ContinueStmt>(S)->getLoc(), "continue statement");
return;
return emitContinueStmt(cast<ContinueStmt>(S));
}
llvm_unreachable("bad statement kind!");
}
@@ -136,12 +134,29 @@ void IRGenFunction::emitReturnStmt(ReturnStmt *S) {
Builder.ClearInsertionPoint();
}
static void emitOrDeleteBlock(IRGenFunction &IGF, llvm::BasicBlock *BB) {
if (BB->use_empty()) {
// If the block is unused, we don't need it; just delete it.
delete BB;
} else {
// Otherwise, continue emitting code in BB.
if (IGF.Builder.hasValidIP())
IGF.Builder.CreateBr(BB);
IGF.Builder.emitBlockAnywhere(BB);
}
}
void IRGenFunction::emitWhileStmt(WhileStmt *S) {
// Create a new basic block and jump into it.
llvm::BasicBlock *loopBB = createBasicBlock("while");
Builder.CreateBr(loopBB);
Builder.emitBlock(loopBB);
// Set the destinations for 'break' and 'continue'
llvm::BasicBlock *endBB = createBasicBlock("while.end");
BreakDestStack.emplace_back(endBB, getCleanupsDepth());
ContinueDestStack.emplace_back(loopBB, getCleanupsDepth());
// Evaluate the condition with the false edge leading directly
// to the continuation block.
Condition cond = emitCondition(S->getCond(), /*hasFalseCode*/ false);
@@ -159,6 +174,10 @@ void IRGenFunction::emitWhileStmt(WhileStmt *S) {
// Complete the conditional execution.
cond.complete(*this);
emitOrDeleteBlock(*this, endBB);
BreakDestStack.pop_back();
ContinueDestStack.pop_back();
}
void IRGenFunction::emitForStmt(ForStmt *S) {
@@ -185,6 +204,12 @@ void IRGenFunction::emitForStmt(ForStmt *S) {
Builder.CreateBr(loopBB);
Builder.emitBlock(loopBB);
// Set the destinations for 'break' and 'continue'
llvm::BasicBlock *incBB = createBasicBlock("for.inc");
llvm::BasicBlock *endBB = createBasicBlock("for.end");
BreakDestStack.emplace_back(endBB, getCleanupsDepth());
ContinueDestStack.emplace_back(incBB, getCleanupsDepth());
// Evaluate the condition with the false edge leading directly
// to the continuation block.
Condition cond = S->getCond().isNonNull() ?
@@ -196,6 +221,8 @@ void IRGenFunction::emitForStmt(ForStmt *S) {
cond.enterTrue(*this);
emitStmt(S->getBody());
emitOrDeleteBlock(*this, incBB);
if (Builder.hasValidIP() && !S->getIncrement().isNull()) {
if (Expr *E = S->getIncrement().dyn_cast<Expr*>()) {
FullExpr scope(*this);
@@ -215,6 +242,9 @@ void IRGenFunction::emitForStmt(ForStmt *S) {
// Complete the conditional execution.
cond.complete(*this);
emitOrDeleteBlock(*this, endBB);
BreakDestStack.pop_back();
ContinueDestStack.pop_back();
}
void IRGenFunction::emitForEachStmt(ForEachStmt *S) {
@@ -227,9 +257,14 @@ void IRGenFunction::emitForEachStmt(ForEachStmt *S) {
if (!Builder.hasValidIP()) return;
// Create a new basic block and jump into it.
llvm::BasicBlock *LoopBB = createBasicBlock("foreach.cond");
Builder.CreateBr(LoopBB);
Builder.emitBlock(LoopBB);
llvm::BasicBlock *loopBB = createBasicBlock("foreach.cond");
Builder.CreateBr(loopBB);
Builder.emitBlock(loopBB);
// Set the destinations for 'break' and 'continue'
llvm::BasicBlock *endBB = createBasicBlock("foreach.end");
BreakDestStack.emplace_back(endBB, getCleanupsDepth());
ContinueDestStack.emplace_back(loopBB, getCleanupsDepth());
Condition Cond = emitCondition(S->getRangeEmpty(), /*hasFalseCode=*/false,
/*invertValue=*/true);
@@ -247,7 +282,7 @@ void IRGenFunction::emitForEachStmt(ForEachStmt *S) {
// Loop back to the header.
if (Builder.hasValidIP()) {
Builder.CreateBr(LoopBB);
Builder.CreateBr(loopBB);
Builder.ClearInsertionPoint();
}
Cond.exitTrue(*this);
@@ -255,5 +290,18 @@ void IRGenFunction::emitForEachStmt(ForEachStmt *S) {
// Complete the conditional execution.
Cond.complete(*this);
emitOrDeleteBlock(*this, endBB);
BreakDestStack.pop_back();
ContinueDestStack.pop_back();
}
void IRGenFunction::emitBreakStmt(BreakStmt *S) {
emitBranch(BreakDestStack.back());
Builder.ClearInsertionPoint();
}
void IRGenFunction::emitContinueStmt(ContinueStmt *S) {
emitBranch(ContinueDestStack.back());
Builder.ClearInsertionPoint();
}

View File

@@ -23,6 +23,7 @@
#include "llvm/ADT/DenseMap.h"
#include "DiverseStack.h"
#include "IRBuilder.h"
#include "JumpDest.h"
namespace llvm {
class AllocaInst;
@@ -33,7 +34,9 @@ namespace llvm {
namespace swift {
class AssignStmt;
class BraceStmt;
class BreakStmt;
class ClassType;
class ContinueStmt;
class Decl;
class Expr;
class ExtensionDecl;
@@ -56,13 +59,11 @@ namespace swift {
class WhileStmt;
namespace irgen {
class Cleanup;
class Condition;
class Explosion;
enum class ExplosionKind : unsigned;
class HeapLayout;
class IRGenModule;
class JumpDest;
class LinkEntity;
class LValue;
class ManagedValue;
@@ -175,8 +176,6 @@ public:
return pushCleanupInState<T, A...>(state, ::std::forward<A>(args)...);
}
typedef DiverseStackImpl<Cleanup>::stable_iterator CleanupsDepth;
/// Retun a stable reference to the current cleanup.
CleanupsDepth getCleanupsDepth() const {
return Cleanups.stable_begin();
@@ -208,6 +207,8 @@ private:
llvm::BasicBlock *UnreachableBB;
llvm::Instruction *JumpDestSlot;
CleanupsDepth InnermostScope;
std::vector<JumpDest> BreakDestStack;
std::vector<JumpDest> ContinueDestStack;
friend class Scope;
Cleanup &initCleanup(Cleanup &cleanup, size_t allocSize, CleanupState state);
@@ -276,6 +277,8 @@ private:
void emitWhileStmt(WhileStmt *S);
void emitForStmt(ForStmt *S);
void emitForEachStmt(ForEachStmt *S);
void emitBreakStmt(BreakStmt *S);
void emitContinueStmt(ContinueStmt *S);
//--- Expression emission ------------------------------------------------------
public:

View File

@@ -17,7 +17,7 @@
#ifndef SWIFT_IRGEN_JUMPDEST_H
#define SWIFT_IRGEN_JUMPDEST_H
#include "IRGenFunction.h"
#include "DiverseStack.h"
namespace llvm {
class BasicBlock;
@@ -26,18 +26,21 @@ namespace llvm {
namespace swift {
namespace irgen {
class Cleanup;
typedef DiverseStackImpl<Cleanup>::stable_iterator CleanupsDepth;
/// The destination of a direct jump. Swift currently does not
/// support indirect branches or goto, so the jump mechanism only
/// needs to worry about branches into scopes, not out of them.
class JumpDest {
llvm::BasicBlock *Block;
IRGenFunction::CleanupsDepth Depth;
CleanupsDepth Depth;
public:
JumpDest(llvm::BasicBlock *block, IRGenFunction::CleanupsDepth depth)
JumpDest(llvm::BasicBlock *block, CleanupsDepth depth)
: Block(block), Depth(depth) {}
llvm::BasicBlock *getBlock() const { return Block; }
IRGenFunction::CleanupsDepth getDepth() const { return Depth; }
CleanupsDepth getDepth() const { return Depth; }
};
} // end namespace irgen

View File

@@ -26,8 +26,8 @@ namespace irgen {
/// statement) has been entered.
class Scope {
IRGenFunction &IGF;
IRGenFunction::CleanupsDepth Depth;
IRGenFunction::CleanupsDepth SavedInnermostScope;
CleanupsDepth Depth;
CleanupsDepth SavedInnermostScope;
void popImpl() {
IGF.Cleanups.checkIterator(Depth);
@@ -51,7 +51,7 @@ public:
void pop() {
assert(Depth.isValid() && "popping a scope twice!");
popImpl();
Depth = IRGenFunction::CleanupsDepth::invalid();
Depth = CleanupsDepth::invalid();
}
~Scope() {

View File

@@ -0,0 +1,134 @@
// RUN: %swift -i %s | FileCheck %s
func test1() {
println("test1")
var i : Int
for i=0;;++i {
if i > 2 {
break
}
println(i)
continue
}
}
func test2() {
println("test2")
var i : Int
for i=0;i<10;++i {
if i > 2 {
continue
}
println(i)
}
}
func test3() {
println("test3")
var i : Int
for i=0;i<10;++i {
if i > 2 {
break
}
println(i)
}
}
func test4() {
println("test4")
for i in 0..10 {
if i > 2 {
break
}
println(i)
}
}
func test5() {
println("test5")
for i in 0..10 {
if i < 2 {
println(i)
continue
}
return
}
}
func test6() {
println("test6")
var i = 0
while (i < 10) {
if i < 2 {
println(i)
++i
continue
}
return
}
}
func test7() {
println("test7")
var i = 0
while (i < 10) {
if i < 2 {
println(i)
break
}
return
}
println("foo")
}
func test8() {
println("test8")
var i : Int
for i=0;;++i {
for j in 0..10 {
if j > 1 {
break
}
println(j)
}
if i > 2 {
break
}
println(i)
continue
}
}
println("start")
test1()
// CHECK: test1
// CHECK-NEXT: 0
// CHECK-NEXT: 1
// CHECK-NEXT: 2
test2()
// CHECK: test2
// CHECK-NEXT: 0
// CHECK-NEXT: 1
// CHECK-NEXT: 2
test3()
// CHECK: test3
// CHECK-NEXT: 0
// CHECK-NEXT: 1
// CHECK-NEXT: 2
test4()
// CHECK: test4
// CHECK-NEXT: 0
// CHECK-NEXT: 1
// CHECK-NEXT: 2
test5()
// CHECK: test5
// CHECK-NEXT: 0
// CHECK-NEXT: 1
test6()
// CHECK: test6
// CHECK-NEXT: 0
// CHECK-NEXT: 1
test7()
// CHECK: test7
// CHECK-NEXT: 0
// CHECK-NEXT: foo
test8()
// CHECK: test8
// CHECK-NEXT: 0
// CHECK-NEXT: 1
// CHECK-NEXT: 0
// CHECK-NEXT: 0
// CHECK-NEXT: 1
// CHECK-NEXT: 1