diff --git a/docs/SIL.rst b/docs/SIL.rst index 8dcdb312c6add..ae3360c569c84 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -3079,7 +3079,7 @@ alloc_stack ``````````` :: - sil-instruction ::= 'alloc_stack' '[dynamic_lifetime]'? sil-type (',' debug-var-attr)* + sil-instruction ::= 'alloc_stack' '[dynamic_lifetime]'? '[lexical]'? sil-type (',' debug-var-attr)* %1 = alloc_stack $T // %1 has type $*T @@ -3102,6 +3102,9 @@ The ``dynamic_lifetime`` attribute specifies that the initialization and destruction of the stored value cannot be verified at compile time. This is the case, e.g. for conditionally initialized objects. +The optional ``lexical`` attribute specifies that the operand corresponds to a +local variable in the Swift source. + The memory is not retainable. To allocate a retainable box for a value type, use ``alloc_box``. diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index facf575b79b8e..b8a95db4f474f 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -365,13 +365,14 @@ class SILBuilder { AllocStackInst *createAllocStack(SILLocation Loc, SILType elementType, Optional Var = None, - bool hasDynamicLifetime = false) { + bool hasDynamicLifetime = false, + bool isLexical = false) { Loc.markAsPrologue(); assert((!dyn_cast_or_null(Loc.getAsASTNode()) || Var) && "location is a VarDecl, but SILDebugVariable is empty"); return insert(AllocStackInst::create(getSILDebugLocation(Loc), elementType, - getFunction(), - Var, hasDynamicLifetime)); + getFunction(), Var, hasDynamicLifetime, + isLexical)); } AllocRefInst *createAllocRef(SILLocation Loc, SILType ObjectType, diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index a05a5c5c6c074..3d8aa161249db 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -759,9 +759,9 @@ SILCloner::visitAllocStackInst(AllocStackInst *Inst) { Loc = MandatoryInlinedLocation::getAutoGeneratedLocation(); VarInfo = None; } - auto *NewInst = - getBuilder().createAllocStack(Loc, getOpType(Inst->getElementType()), - VarInfo, Inst->hasDynamicLifetime()); + auto *NewInst = getBuilder().createAllocStack( + Loc, getOpType(Inst->getElementType()), VarInfo, + Inst->hasDynamicLifetime(), Inst->isLexical()); remapDebugVarInfo(DebugVarCarryingInst(NewInst)); recordClonedInstruction(Inst, NewInst); } diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index a588bd4dc07a4..7dbae743ac032 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -1902,16 +1902,16 @@ class AllocStackInst final friend SILBuilder; bool dynamicLifetime = false; + bool lexical = false; AllocStackInst(SILDebugLocation Loc, SILType elementType, - ArrayRef TypeDependentOperands, - SILFunction &F, - Optional Var, bool hasDynamicLifetime); + ArrayRef TypeDependentOperands, SILFunction &F, + Optional Var, bool hasDynamicLifetime, + bool isLexical); static AllocStackInst *create(SILDebugLocation Loc, SILType elementType, - SILFunction &F, - Optional Var, - bool hasDynamicLifetime); + SILFunction &F, Optional Var, + bool hasDynamicLifetime, bool isLexical); SIL_DEBUG_VAR_SUPPLEMENT_TRAILING_OBJS_IMPL() @@ -1942,6 +1942,9 @@ class AllocStackInst final /// is conditionally initialized. bool hasDynamicLifetime() const { return dynamicLifetime; } + /// Whether the alloc_stack instruction corresponds to a source-level VarDecl. + bool isLexical() const { return lexical; } + /// Return the debug variable information attached to this instruction. Optional getVarInfo() const { Optional AuxVarType; diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 8d289b67b0c6d..dd099e2b50656 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -191,13 +191,13 @@ SILDebugVariable::createFromAllocation(const AllocationInst *AI) { AllocStackInst::AllocStackInst(SILDebugLocation Loc, SILType elementType, ArrayRef TypeDependentOperands, SILFunction &F, Optional Var, - bool hasDynamicLifetime) + bool hasDynamicLifetime, bool isLexical) : InstructionBase(Loc, elementType.getAddressType()), SILDebugVariableSupplement(Var ? Var->DIExpr.getNumElements() : 0, Var ? Var->Type.hasValue() : false, Var ? Var->Loc.hasValue() : false, Var ? Var->Scope != nullptr : false), - dynamicLifetime(hasDynamicLifetime) { + dynamicLifetime(hasDynamicLifetime), lexical(isLexical) { SILNode::Bits.AllocStackInst.NumOperands = TypeDependentOperands.size(); assert(SILNode::Bits.AllocStackInst.NumOperands == @@ -218,19 +218,18 @@ AllocStackInst::AllocStackInst(SILDebugLocation Loc, SILType elementType, TypeDependentOperands); } -AllocStackInst * -AllocStackInst::create(SILDebugLocation Loc, - SILType elementType, SILFunction &F, - Optional Var, - bool hasDynamicLifetime) { +AllocStackInst *AllocStackInst::create(SILDebugLocation Loc, + SILType elementType, SILFunction &F, + Optional Var, + bool hasDynamicLifetime, + bool isLexical) { SmallVector TypeDependentOperands; collectTypeDependentOperands(TypeDependentOperands, F, elementType.getASTType()); void *Buffer = allocateDebugVarCarryingInst( F.getModule(), Var, TypeDependentOperands); - return ::new (Buffer) - AllocStackInst(Loc, elementType, TypeDependentOperands, F, Var, - hasDynamicLifetime); + return ::new (Buffer) AllocStackInst(Loc, elementType, TypeDependentOperands, + F, Var, hasDynamicLifetime, isLexical); } VarDecl *AllocationInst::getDecl() const { diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 60880e364d217..ac62f83dab47f 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -1260,6 +1260,8 @@ class SILPrinter : public SILInstructionVisitor { void visitAllocStackInst(AllocStackInst *AVI) { if (AVI->hasDynamicLifetime()) *this << "[dynamic_lifetime] "; + if (AVI->isLexical()) + *this << "[lexical] "; *this << AVI->getElementType(); printDebugVar(AVI->getVarInfo(), &AVI->getModule().getASTContext().SourceMgr); diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 805447371252d..241867d4a56ec 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -4108,34 +4108,60 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, break; } - case SILInstructionKind::AllocStackInst: - case SILInstructionKind::MetatypeInst: { - + case SILInstructionKind::AllocStackInst: { bool hasDynamicLifetime = false; - if (Opcode == SILInstructionKind::AllocStackInst && - parseSILOptional(hasDynamicLifetime, *this, "dynamic_lifetime")) + bool isLexical = false; + + while (P.consumeIf(tok::l_square)) { + Identifier ident; + SourceLoc identLoc; + if (parseSILIdentifier(ident, identLoc, + diag::expected_in_attribute_list)) { + if (P.consumeIf(tok::r_square)) { + continue; + } else { + return true; + } + } + StringRef attr = ident.str(); + + if (attr == "dynamic_lifetime") { + hasDynamicLifetime = true; + } else if (attr == "lexical") { + isLexical = true; + } else { + return true; + } + + if (!P.consumeIf(tok::r_square)) + return true; + } + + SILType Ty; + if (parseSILType(Ty)) return true; + SILDebugVariable VarInfo; + if (parseSILDebugVar(VarInfo) || parseSILDebugLocation(InstLoc, B)) + return true; + // It doesn't make sense to attach a debug var info if the name is empty + if (VarInfo.Name.size()) + ResultVal = B.createAllocStack(InstLoc, Ty, VarInfo, hasDynamicLifetime, + isLexical); + else + ResultVal = + B.createAllocStack(InstLoc, Ty, {}, hasDynamicLifetime, isLexical); + break; + } + case SILInstructionKind::MetatypeInst: { SILType Ty; if (parseSILType(Ty)) return true; - if (Opcode == SILInstructionKind::AllocStackInst) { - SILDebugVariable VarInfo; - if (parseSILDebugVar(VarInfo) || parseSILDebugLocation(InstLoc, B)) - return true; - // It doesn't make sense to attach a debug var info if the name is empty - if (VarInfo.Name.size()) - ResultVal = - B.createAllocStack(InstLoc, Ty, VarInfo, hasDynamicLifetime); - else - ResultVal = B.createAllocStack(InstLoc, Ty, {}, hasDynamicLifetime); - } else { - assert(Opcode == SILInstructionKind::MetatypeInst); - if (parseSILDebugLocation(InstLoc, B)) - return true; - ResultVal = B.createMetatype(InstLoc, Ty); - } + assert(Opcode == SILInstructionKind::MetatypeInst); + if (parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createMetatype(InstLoc, Ty); break; } case SILInstructionKind::AllocRefInst: diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index a752f33451622..57469d49b1745 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1217,12 +1217,15 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, Loc, cast(MF->getType(TyID)->getCanonicalType()), None, /*bool hasDynamicLifetime*/ Attr != 0); break; - case SILInstructionKind::AllocStackInst: + case SILInstructionKind::AllocStackInst: { assert(RecordKind == SIL_ONE_TYPE && "Layout should be OneType."); + bool hasDynamicLifetime = Attr & 0x1; + bool isLexical = (Attr >> 1) & 0x1; ResultInst = Builder.createAllocStack( Loc, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory, Fn), - None, /*bool hasDynamicLifetime*/ Attr != 0); + None, hasDynamicLifetime, isLexical); break; + } case SILInstructionKind::MetatypeInst: assert(RecordKind == SIL_ONE_TYPE && "Layout should be OneType."); ResultInst = Builder.createMetatype( diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index edbb5c658f091..3b8ac0ba3dd00 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -992,8 +992,9 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { } case SILInstructionKind::AllocStackInst: { const AllocStackInst *ASI = cast(&SI); - writeOneTypeLayout(ASI->getKind(), ASI->hasDynamicLifetime() ? 1 : 0, - ASI->getElementType()); + unsigned attr = + unsigned(ASI->hasDynamicLifetime()) + unsigned(ASI->isLexical() << 1); + writeOneTypeLayout(ASI->getKind(), attr, ASI->getElementType()); break; } case SILInstructionKind::ProjectValueBufferInst: { diff --git a/test/SIL/Parser/basic.sil b/test/SIL/Parser/basic.sil index f4a494071ceb5..619fecbce783d 100644 --- a/test/SIL/Parser/basic.sil +++ b/test/SIL/Parser/basic.sil @@ -1230,6 +1230,27 @@ bb0: return %2 : $() } +// CHECK-LABEL: sil @test_alloc_stack_flags +sil @test_alloc_stack_flags : $() -> () { + // CHECK: alloc_stack $Builtin.NativeObjec + %instance = alloc_stack $Builtin.NativeObject + dealloc_stack %instance : $*Builtin.NativeObject + // CHECK: alloc_stack [dynamic_lifetime] + %instance2 = alloc_stack [dynamic_lifetime] $Builtin.NativeObject + dealloc_stack %instance2 : $*Builtin.NativeObject + // CHECK: alloc_stack [lexical] + %instance3 = alloc_stack [lexical] $Builtin.NativeObject + dealloc_stack %instance3 : $*Builtin.NativeObject + // CHECK: alloc_stack [dynamic_lifetime] [lexical] + %instance4 = alloc_stack [dynamic_lifetime] [lexical] $Builtin.NativeObject + dealloc_stack %instance4 : $*Builtin.NativeObject + // CHECK: alloc_stack [dynamic_lifetime] [lexical] + %instance5 = alloc_stack [lexical] [dynamic_lifetime] $Builtin.NativeObject + dealloc_stack %instance5 : $*Builtin.NativeObject + %res = tuple () + return %res : $() +} + sil_global @staticProp: $Int // CHECK-LABEL: sil private @globalinit_func0 : $@convention(thin) () -> () { diff --git a/test/SIL/Serialization/borrow.sil b/test/SIL/Serialization/borrow.sil index cdb8725d165f2..0362b1b572c76 100644 --- a/test/SIL/Serialization/borrow.sil +++ b/test/SIL/Serialization/borrow.sil @@ -8,6 +8,40 @@ sil_stage canonical import Builtin +// CHECK-LABEL: sil [serialized] [ossa] @begin_borrow_test +// CHECK: begin_borrow [defined] {{%[^,]+}} +// CHECK: begin_borrow {{%[^,]+}} +// CHECK: } // end sil function 'begin_borrow_test' +sil [serialized] [ossa] @begin_borrow_test : $@convention(thin) () -> () { + %instance = alloc_ref $C + %guaranteed_c = begin_borrow [defined] %instance : $C + end_borrow %guaranteed_c : $C + %guaranteed_c2 = begin_borrow %instance : $C + end_borrow %guaranteed_c2 : $C + destroy_value %instance : $C + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [serialized] [ossa] @alloc_stack_test +// CHECK: alloc_stack $Builtin.NativeObject +// CHECK: alloc_stack [dynamic_lifetime] +// CHECK: alloc_stack [lexical] +// CHECK: alloc_stack [dynamic_lifetime] [lexical] +// CHECK: } // end sil function 'alloc_stack_test' +sil [serialized] [ossa] @alloc_stack_test : $@convention(thin) () -> () { + %instance = alloc_stack $Builtin.NativeObject + dealloc_stack %instance : $*Builtin.NativeObject + %instance2 = alloc_stack [dynamic_lifetime] $Builtin.NativeObject + dealloc_stack %instance2 : $*Builtin.NativeObject + %instance3 = alloc_stack [lexical] $Builtin.NativeObject + dealloc_stack %instance3 : $*Builtin.NativeObject + %instance4 = alloc_stack [lexical] [dynamic_lifetime] $Builtin.NativeObject + dealloc_stack %instance4 : $*Builtin.NativeObject + %res = tuple () + return %res : $() +} + // We do not verify here, but just make sure that all of the combinations parse and print correctly. // CHECK-LABEL: sil [serialized] [ossa] @borrow_test : $@convention(thin) (@in Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () { // CHECK: bb0([[ARG1:%[0-9]+]] : $*Builtin.NativeObject, [[ARG2:%[0-9]+]] : @guaranteed $Builtin.NativeObject): @@ -31,15 +65,3 @@ bb0(%0 : $*Builtin.NativeObject, %1 : @guaranteed $Builtin.NativeObject): } class C {} - -// CHECK-LABEL: sil [ossa] @defined_borrow_test -// CHECK: begin_borrow [defined] {{%[^,]+}} -// CHECK-LABEL: } // end sil function 'defined_borrow_test' -sil [ossa] @defined_borrow_test : $@convention(thin) () -> () { - %instance = alloc_ref $C - %guaranteed_c = begin_borrow [defined] %instance : $C - end_borrow %guaranteed_c : $C - destroy_value %instance : $C - %res = tuple () - return %res : $() -} diff --git a/test/SIL/cloning.sil b/test/SIL/cloning.sil index 1f1b84375d812..46c472dd4e313 100644 --- a/test/SIL/cloning.sil +++ b/test/SIL/cloning.sil @@ -45,3 +45,28 @@ sil [ossa] @caller_begin_borrow_defined : $@convention(thin) () -> () { %res = apply %callee_begin_borrow_defined() : $@convention(thin) () -> () return %res : $() } + +sil [ossa] @callee_alloc_stack : $@convention(thin) () -> () { + %instance = alloc_stack $Builtin.NativeObject + dealloc_stack %instance : $*Builtin.NativeObject + %instance2 = alloc_stack [dynamic_lifetime] $Builtin.NativeObject + dealloc_stack %instance2 : $*Builtin.NativeObject + %instance3 = alloc_stack [lexical] $Builtin.NativeObject + dealloc_stack %instance3 : $*Builtin.NativeObject + %instance4 = alloc_stack [dynamic_lifetime] [lexical] $Builtin.NativeObject + dealloc_stack %instance4 : $*Builtin.NativeObject + %res = tuple () + return %res : $() +} + +// CHECK-LABEL: sil [ossa] @caller_alloc_stack_lexical +// CHECK: alloc_stack +// CHECK: alloc_stack [dynamic_lifetime] +// CHECK: alloc_stack [lexical] +// CHECK: alloc_stack [dynamic_lifetime] [lexical] +// CHECK-LABEL: } // end sil function 'caller_alloc_stack_lexical' +sil [ossa] @caller_alloc_stack_lexical : $@convention(thin) () -> () { + %callee_alloc_stack = function_ref @callee_alloc_stack : $@convention(thin) () -> () + %res = apply %callee_alloc_stack() : $@convention(thin) () -> () + return %res : $() +}