diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index bf44a76bad0..57b63ca1574 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -126,14 +126,14 @@ class Decl { // The following flags are not necessarily meaningful for all // kinds of value-declarations. - /// Has this declaration been used as an l-value, other than by - /// immediately loading it? - unsigned UsedAsLValue : 1; + // NeverUsedAsLValue - Whether this decl is ever used as an lvalue + // (i.e. used in a context where it could be modified). + unsigned NeverUsedAsLValue : 1; - /// Has this declaration been used as a heap l-value, other than - /// by immediating loading it or converting it to a non-heap - /// l-value? - unsigned UsedAsHeapLValue : 1; + // HasFixedLifetime - Whether the lifetime of this decl matches its + // scope (i.e. the decl isn't captured, so it can be allocated as part of + // the stack frame.) + unsigned HasFixedLifetime : 1; }; enum { NumValueDeclBits = NumNamedDeclBits + 2 }; static_assert(NumValueDeclBits <= 32, "fits in an unsigned"); @@ -305,8 +305,8 @@ class ValueDecl : public NamedDecl { protected: ValueDecl(DeclKind K, DeclContext *DC, Identifier name, Type ty) : NamedDecl(K, DC, name), Ty(ty) { - ValueDeclBits.UsedAsLValue = false; - ValueDeclBits.UsedAsHeapLValue = false; + ValueDeclBits.NeverUsedAsLValue = false; + ValueDeclBits.HasFixedLifetime = false; } public: @@ -345,35 +345,18 @@ public: return getKind() == DeclKind::Var || getKind() == DeclKind::ElementRef; } - /// flagUseAsLValue - Record that the given declaration is known to - /// be used as an l-value, but not necessarily a heap l-value. - void flagUseAsLValue() { - ValueDeclBits.UsedAsLValue = true; + void setHasFixedLifetime(bool flag) { + ValueDeclBits.HasFixedLifetime = flag; + } + void setNeverUsedAsLValue(bool flag) { + ValueDeclBits.NeverUsedAsLValue = flag; } - /// flagUseAsHeapLValue - Record that the given declaration is known - /// to be used as a heap l-value. - void flagUseAsHeapLValue() { - ValueDeclBits.UsedAsLValue = true; - ValueDeclBits.UsedAsHeapLValue = true; + bool hasFixedLifetime() const { + return ValueDeclBits.HasFixedLifetime; } - - /// Is this declaration known to be used somewhere as an l-value? A - /// 'false' answer is not definitive unless it's known that all - /// possible references to the declaration have been seen, as might - /// be true of (say) a local variable after typechecking is - /// complete. - bool hasUseAsLValue() const { - return ValueDeclBits.UsedAsLValue; - } - - /// Is this declaration known to be used somewhere as a heap - /// l-value? A 'false' answer is not definitive unless it's known - /// that all possible references to the declaration have been seen, - /// as might be true of (say) a local variable after typechecking is - /// complete. - bool hasUseAsHeapLValue() const { - return ValueDeclBits.UsedAsHeapLValue; + bool isNeverUsedAsLValue() const { + return ValueDeclBits.NeverUsedAsLValue; } // Implement isa/cast/dyncast/etc. diff --git a/include/swift/Subsystems.h b/include/swift/Subsystems.h index 489f0bd87ef..0f754aaadf3 100644 --- a/include/swift/Subsystems.h +++ b/include/swift/Subsystems.h @@ -45,6 +45,11 @@ namespace swift { /// void performTypeChecking(TranslationUnit *TU); + /// performCaptureAnalysis - Analyse the AST and mark local declarations + /// and expressions which can capture them so they can be emitted more + /// efficiently. + void performCaptureAnalysis(TranslationUnit *TU); + /// performIRGeneration - Turn the given translation unit into /// either LLVM IR or native code. void performIRGeneration(TranslationUnit *TU, irgen::Options &Opts); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 8c686c75d60..78415b03f51 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -297,8 +297,8 @@ namespace { OS << ""; OS << '\''; - if (VD->hasUseAsHeapLValue()) OS << " usedAsHeapLValue=true"; - else if (VD->hasUseAsLValue()) OS << " usedAsLValue=true"; + if (VD->hasFixedLifetime()) OS << " hasFixedLifetime=true"; + if (VD->isNeverUsedAsLValue()) OS << " neverUsedAsLValue=true"; } diff --git a/lib/IRGen/GenFunc.cpp b/lib/IRGen/GenFunc.cpp index d81f2996ea7..3f3323672af 100644 --- a/lib/IRGen/GenFunc.cpp +++ b/lib/IRGen/GenFunc.cpp @@ -957,8 +957,8 @@ OwnedAddress IRGenFunction::getAddrForParameter(VarDecl *param, paramSchema.begin()->getAggregateType()->getPointerTo()); Address paramAddr(addr, paramType.StorageAlignment); - // If it's not referenced on the heap, we can use that directly. - if (!param->hasUseAsHeapLValue()) + // If the lifetime can't be extended, just use the address directly. + if (param->hasFixedLifetime()) return OwnedAddress(paramAddr, IGM.RefCountedNull); // Otherwise, we might have to move it to the heap. @@ -972,8 +972,8 @@ OwnedAddress IRGenFunction::getAddrForParameter(VarDecl *param, // Otherwise, make an alloca and load into it. OwnedAddress paramAddr = createScopeAlloca(paramType, - param->hasUseAsHeapLValue() - ? OnHeap : NotOnHeap, + param->hasFixedLifetime() + ? NotOnHeap : OnHeap, name + ".addr"); // FIXME: This way of getting a list of arguments claimed by storeExplosion diff --git a/lib/IRGen/GenLocal.cpp b/lib/IRGen/GenLocal.cpp index 60fa202b2bd..d3fc3271881 100644 --- a/lib/IRGen/GenLocal.cpp +++ b/lib/IRGen/GenLocal.cpp @@ -57,8 +57,8 @@ void IRGenFunction::emitLocalVar(VarDecl *var) { const TypeInfo &typeInfo = getFragileTypeInfo(var->getType()); OwnedAddress addr = createScopeAlloca(typeInfo, - var->hasUseAsHeapLValue() - ? OnHeap : NotOnHeap, + var->hasFixedLifetime() + ? NotOnHeap : OnHeap, var->getName().str()); setLocal(var, addr); diff --git a/lib/IRGen/IRGenFunction.cpp b/lib/IRGen/IRGenFunction.cpp index fce5841072f..4a4fe03cf3d 100644 --- a/lib/IRGen/IRGenFunction.cpp +++ b/lib/IRGen/IRGenFunction.cpp @@ -44,18 +44,7 @@ IRGenFunction::~IRGenFunction() { /// be of i8* type. void IRGenFunction::emitMemCpy(llvm::Value *dest, llvm::Value *src, Size size, Alignment align) { - dest = Builder.CreateBitCast(dest, IGM.Int8PtrTy); - src = Builder.CreateBitCast(src, IGM.Int8PtrTy); - - llvm::Value *args[] = { - dest, - src, - llvm::ConstantInt::get(IGM.SizeTy, size.getValue()), - llvm::ConstantInt::get(IGM.SizeTy, align.getValue()), - Builder.getFalse() // volatile - }; - - Builder.CreateCall(IGM.getMemCpyFn(), args); + Builder.CreateMemCpy(dest, src, size.getValue(), align.getValue(), false); } void IRGenFunction::unimplemented(SourceLoc Loc, StringRef Message) { diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index ca84d24a299..75092740963 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -72,15 +72,6 @@ IRGenModule::~IRGenModule() { delete &Types; } -llvm::Constant *IRGenModule::getMemCpyFn() { - if (MemCpyFn) return MemCpyFn; - - llvm::Type *types[] = { SizeTy }; - MemCpyFn = llvm::Intrinsic::getDeclaration(&Module, llvm::Intrinsic::memcpy, - types); - return MemCpyFn; -} - llvm::Constant *IRGenModule::getAllocFn() { if (AllocFn) return AllocFn; diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 196fb637666..7b08ecddc08 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -115,7 +115,6 @@ private: //--- Runtime --------------------------------------------------------------- public: - llvm::Constant *getMemCpyFn(); llvm::Constant *getAllocFn(); llvm::Constant *getRetainFn(); llvm::Constant *getReleaseFn(); diff --git a/lib/Sema/CaptureAnalysis.cpp b/lib/Sema/CaptureAnalysis.cpp new file mode 100644 index 00000000000..9a6d547f772 --- /dev/null +++ b/lib/Sema/CaptureAnalysis.cpp @@ -0,0 +1,167 @@ +//===--- CaptureAnalysis.cpp - Analyze capture properties -----------------===// +// +// 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 analysis for local variables and expressions which can +// capture them, to optimize code generation. +// +//===----------------------------------------------------------------------===// + +#include "swift/Subsystems.h" +#include "swift/AST/ASTWalker.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Expr.h" +#include "swift/AST/Module.h" +#include "swift/AST/Pattern.h" +#include "swift/AST/Stmt.h" +#include "swift/AST/Types.h" + +using namespace swift; + +static void VisitValueDecl(ValueDecl *VD) { + if (VD->getDeclContext()->isLocalContext()) { + // We assume that these flags are correct unless + // we show otherwise in walkToExprPre. + VD->setNeverUsedAsLValue(true); + VD->setHasFixedLifetime(true); + } +} + +// Find ValueDecls in the given pattern. +static void WalkPattern(Pattern *P) { + switch (P->getKind()) { + case PatternKind::Tuple: + for (auto &field : cast(P)->getFields()) + WalkPattern(field.getPattern()); + return; + + case PatternKind::Paren: + return WalkPattern(cast(P)->getSubPattern()); + + case PatternKind::Typed: + return WalkPattern(cast(P)->getSubPattern()); + + case PatternKind::Named: + VisitValueDecl(cast(P)->getDecl()); + break; + + case PatternKind::Any: + break; + } +} + +static ValueDecl* FindValueDecl(Expr *E) { + // Strip off expressions which don't matter for this analysis. + while (1) { + if (ParenExpr *PE = dyn_cast(E)) + E = PE->getSubExpr(); + else if (TupleElementExpr *TE = dyn_cast(E)) + E = TE->getBase(); + else if (LookThroughOneofExpr *LTOE = dyn_cast(E)) + E = LTOE->getSubExpr(); + else if (AddressOfExpr *AOE = dyn_cast(E)) + E = AOE->getSubExpr(); + else + break; + } + // Return the found DeclRefExpr. + if (DeclRefExpr *DRE = dyn_cast(E)) + return DRE->getDecl(); + return 0; +} + +namespace { +// This recursive visitor implements two rules: +// +// 1. A local variable's lifetime is fixed (i.e. the variable can be +// emitted on the stack) if it isn't captured by a CapturingExpr +// and all DeclRefExprs are operands of known-safe operations +// (specifically, LoadExprs, RequalifyExprs which strip off the +// heap qualifier, and AssignStmts). +// +// 2. A DeclRefExpr referring to a variable is an "lvalue use" if it is not +// the operand of a LoadExpr. +class CaptureAnalysisVisitor : public ASTWalker { + bool walkToExprPre(Expr *E) { + if (LoadExpr *LE = dyn_cast(E)) { + // A DeclRefExpr which is immediately loaded can't extend the lifetime of + // a variable, and can't modify it. + if (FindValueDecl(LE->getSubExpr())) + return false; + } else if (RequalifyExpr *RE = dyn_cast(E)) { + // A DeclRefExpr which has the heap qualifier stripped off can't extend + // the lifetime of a variable. + LValueType *SrcLT = RE->getSubExpr()->getType()->castTo(); + LValueType *DstLT = RE->getType()->castTo(); + if ((DstLT->getQualifiers() & LValueType::Qual::NonHeap) && + !(SrcLT->getQualifiers() & LValueType::Qual::NonHeap)) + if (ValueDecl *D = FindValueDecl(RE->getSubExpr())) { + if (D->getDeclContext()->isLocalContext()) + D->setNeverUsedAsLValue(false); + return false; + } + } else if (DeclRefExpr *DRE = dyn_cast(E)) { + // We can't reason about the decl referred to by a general DeclRefExpr. + ValueDecl *D = DRE->getDecl(); + if (D->getDeclContext()->isLocalContext()) { + D->setNeverUsedAsLValue(false); + D->setHasFixedLifetime(false); + } + } else if (CapturingExpr *CE = dyn_cast(E)) { + // Initialize flags for the function arguments. + if (FuncExpr *FE = dyn_cast(CE)) { + for (Pattern *P : FE->getParamPatterns()) + WalkPattern(P); + } else if (ClosureExpr *CLE = dyn_cast(CE)) { + WalkPattern(CLE->getPattern()); + } + // A variable which is captured might have its lifetime extended. + // FIXME: We can be a lot smarter here if we prove that either the + // CapturingExpr can't be captured or the variable can't be modified + // after it is captured. Proving a CapturingExpr can't be captured + // requires nocapture annotations. IRGen can use the neverUsedAsLValue + // as a trivial form of proving a variable can't be modified; control + // flow analysis plus a way to force IRGen to copy a variable when + // a given expression captures it would allow some more elaborate + // tricks here. + for (ValueDecl *D : CE->getCaptures()) + D->setHasFixedLifetime(false); + } + return true; + } + + bool walkToStmtPre(Stmt *S) { + if (AssignStmt *AS = dyn_cast(S)) { + // An assignment to a variable can't extend its lifetime. + if (ValueDecl *D = FindValueDecl(AS->getDest())) { + if (D->getDeclContext()->isLocalContext()) + D->setNeverUsedAsLValue(false); + + AS->getSrc()->walk(*this); + return false; + } + } + return true; + } + + bool walkToDeclPre(Decl *D) { + if (ValueDecl *VD = dyn_cast(D)) + VisitValueDecl(VD); + + return true; + } +}; + +} // end anonymous namespace + +void swift::performCaptureAnalysis(TranslationUnit *TU) { + TU->Body->walk(CaptureAnalysisVisitor()); +} diff --git a/lib/Sema/TypeCheckCoercion.cpp b/lib/Sema/TypeCheckCoercion.cpp index 29e90f50f55..c2b839bfaeb 100644 --- a/lib/Sema/TypeCheckCoercion.cpp +++ b/lib/Sema/TypeCheckCoercion.cpp @@ -640,10 +640,8 @@ Expr *SemaCoerce::convertToType(Expr *E, Type DestTy, TypeChecker &TC) { } // If we have an exact match, we're done. - if (E->getType()->isEqual(DestTy)) { - TC.markUsesOfLValues(E); + if (E->getType()->isEqual(DestTy)) return E; - } // If the expression is a grouping parenthesis and it has a dependent type, // just force the type through it, regardless of what DestTy is. @@ -663,7 +661,6 @@ Expr *SemaCoerce::convertToType(Expr *E, Type DestTy, TypeChecker &TC) { assert(SrcLT->getQualifiers() < DestLT->getQualifiers() && "qualifiers match exactly but types are different?"); - TC.markUseAsLValue(E, DestLT->isHeap()); return new (TC.Context) RequalifyExpr(E, DestTy); } diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index fd5f8865317..604449c3c15 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -1112,113 +1112,3 @@ bool TypeChecker::typeCheckCondition(Expr *&E) { } return false; } - -/// Given a type-checked expression which yields a value that may or -/// may not be materializable, find any l-value expressions which -/// create part of the expression's result and call markUseAsLValue -/// for them as appropriate. -/// -/// The basic behavior here is simply checking whether the -/// expression's type is an l-value type, except we can have r-value -/// expressions that embed l-values in some cases. This can only -/// happen with tuple literals, but nonetheless it can happen. -void TypeChecker::markUsesOfLValues(Expr *E) { - // Look through parens. - if (ParenExpr *PE = dyn_cast(E)) - return markUsesOfLValues(PE->getSubExpr()); - if (TupleExpr *TE = dyn_cast(E)) { - for (Expr *elt : TE->getElements()) - markUsesOfLValues(elt); - return; - } - if (LValueType *LT = E->getType()->getAs()) - markUseAsLValue(E, LT->isHeap()); -} - -namespace { - class LValueVisitor : public ASTVisitor { - bool AsHeap; - public: - LValueVisitor(bool asHeap) : AsHeap(asHeap) {} - -#define EXPR(Id, Parent) -#define UNCHECKED_EXPR(Id, Parent) \ - void visit##Id##Expr(Id##Expr *E) { \ - llvm_unreachable("trying to mark an unchecked expression"); \ - } -#include "swift/AST/ExprNodes.def" - -#define NON_LVALUE_EXPR(Id) \ - void visit##Id##Expr(Id##Expr *E) { \ - llvm_unreachable("never an l-value"); \ - } - NON_LVALUE_EXPR(IntegerLiteral) - NON_LVALUE_EXPR(FloatLiteral) - NON_LVALUE_EXPR(Apply) - NON_LVALUE_EXPR(Closure) - NON_LVALUE_EXPR(Func) - NON_LVALUE_EXPR(Load) - NON_LVALUE_EXPR(Module) -#undef NON_VALUE_EXPR - - void visitErrorExpr(ErrorExpr *E) {} - - void visitDeclRefExpr(DeclRefExpr *E) { - if (AsHeap) { - E->getDecl()->flagUseAsHeapLValue(); - } else { - E->getDecl()->flagUseAsLValue(); - } - } - - void visitParenExpr(ParenExpr *E) { - visit(E->getSubExpr()); - } - - void visitAddressOfExpr(AddressOfExpr *E) { - visit(E->getSubExpr()); - } - - void visitTupleElementExpr(TupleElementExpr *E) { - visit(E->getBase()); - } - - void visitLookThroughOneofExpr(LookThroughOneofExpr *E) { - visit(E->getSubExpr()); - } - - void visitRequalifyExpr(RequalifyExpr *E) { - visit(E->getSubExpr()); - } - - void visitMaterializeExpr(MaterializeExpr *E) { - // Nothing to mark. - } - - void visitTupleShuffleExpr(TupleShuffleExpr *E) { - // Tuple shuffles can add l-values as defaults. - TupleType *TT = E->getType()->castTo(); - for (unsigned i = 0, e = TT->getFields().size(); i != e; ++i) { - if (E->getElementMapping()[i] == -1) - visit(TT->getFields()[i].getInit()); - } - visit(E->getSubExpr()); - } - - void visitTupleExpr(TupleExpr *E) { - for (auto elt : E->getElements()) - visit(elt); - } - - void visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *E) { - visit(E->getRHS()); - } - }; -} - -/// Given an expression of l-value type, try to mark the ValueDecl -/// which serves as a base for the l-value as having been used as an -/// l-value. -void TypeChecker::markUseAsLValue(Expr *E, bool asHeap) { - LValueVisitor(asHeap).visit(E); -} diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 2ad3342792e..5a9ecba108f 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -70,12 +70,10 @@ public: S->setDest(E); Type lhsTy = E->getType(); - if (LValueType *lvalueTy = lhsTy->getAs()) { - TC.markUseAsLValue(E, /*as heap*/ false); + if (LValueType *lvalueTy = lhsTy->getAs()) lhsTy = lvalueTy->getObjectType(); - } else { + else TC.diagnose(E->getLoc(), diag::assignment_lhs_not_lvalue); - } E = S->getSrc(); if (typeCheckExpr(E, lhsTy)) return 0; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index eff38b5add5..09d84021b95 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -62,9 +62,6 @@ public: Expr *convertToMaterializable(Expr *E); Expr *foldSequence(SequenceExpr *E); - - void markUseAsLValue(Expr *E, bool asHeap); - void markUsesOfLValues(Expr *E); /// diagnoseEmptyOverloadSet - Diagnose a case where we disproved all of the /// possible candidates in an overload set of a call.