Skip to content

Commit

Permalink
[SIL] Added lexical flag to alloc_stack.
Browse files Browse the repository at this point in the history
The new flag will be used to track whether a borrow scope corresponds to
a source-level lexical scope.  Here, the flag is just documented, added
to the instruction, represented in textual and serialized SIL, and
cloned.
  • Loading branch information
nate-chandler committed Sep 14, 2021
1 parent 6738c3e commit b57b222
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 60 deletions.
5 changes: 4 additions & 1 deletion docs/SIL.rst
Expand Up @@ -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
Expand All @@ -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``.

Expand Down
7 changes: 4 additions & 3 deletions include/swift/SIL/SILBuilder.h
Expand Up @@ -365,13 +365,14 @@ class SILBuilder {

AllocStackInst *createAllocStack(SILLocation Loc, SILType elementType,
Optional<SILDebugVariable> Var = None,
bool hasDynamicLifetime = false) {
bool hasDynamicLifetime = false,
bool isLexical = false) {
Loc.markAsPrologue();
assert((!dyn_cast_or_null<VarDecl>(Loc.getAsASTNode<Decl>()) || 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,
Expand Down
6 changes: 3 additions & 3 deletions include/swift/SIL/SILCloner.h
Expand Up @@ -759,9 +759,9 @@ SILCloner<ImplClass>::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);
}
Expand Down
15 changes: 9 additions & 6 deletions include/swift/SIL/SILInstruction.h
Expand Up @@ -1902,16 +1902,16 @@ class AllocStackInst final
friend SILBuilder;

bool dynamicLifetime = false;
bool lexical = false;

AllocStackInst(SILDebugLocation Loc, SILType elementType,
ArrayRef<SILValue> TypeDependentOperands,
SILFunction &F,
Optional<SILDebugVariable> Var, bool hasDynamicLifetime);
ArrayRef<SILValue> TypeDependentOperands, SILFunction &F,
Optional<SILDebugVariable> Var, bool hasDynamicLifetime,
bool isLexical);

static AllocStackInst *create(SILDebugLocation Loc, SILType elementType,
SILFunction &F,
Optional<SILDebugVariable> Var,
bool hasDynamicLifetime);
SILFunction &F, Optional<SILDebugVariable> Var,
bool hasDynamicLifetime, bool isLexical);

SIL_DEBUG_VAR_SUPPLEMENT_TRAILING_OBJS_IMPL()

Expand Down Expand Up @@ -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<SILDebugVariable> getVarInfo() const {
Optional<SILType> AuxVarType;
Expand Down
19 changes: 9 additions & 10 deletions lib/SIL/IR/SILInstructions.cpp
Expand Up @@ -191,13 +191,13 @@ SILDebugVariable::createFromAllocation(const AllocationInst *AI) {
AllocStackInst::AllocStackInst(SILDebugLocation Loc, SILType elementType,
ArrayRef<SILValue> TypeDependentOperands,
SILFunction &F, Optional<SILDebugVariable> 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 ==
Expand All @@ -218,19 +218,18 @@ AllocStackInst::AllocStackInst(SILDebugLocation Loc, SILType elementType,
TypeDependentOperands);
}

AllocStackInst *
AllocStackInst::create(SILDebugLocation Loc,
SILType elementType, SILFunction &F,
Optional<SILDebugVariable> Var,
bool hasDynamicLifetime) {
AllocStackInst *AllocStackInst::create(SILDebugLocation Loc,
SILType elementType, SILFunction &F,
Optional<SILDebugVariable> Var,
bool hasDynamicLifetime,
bool isLexical) {
SmallVector<SILValue, 8> TypeDependentOperands;
collectTypeDependentOperands(TypeDependentOperands, F,
elementType.getASTType());
void *Buffer = allocateDebugVarCarryingInst<AllocStackInst>(
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 {
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/IR/SILPrinter.cpp
Expand Up @@ -1260,6 +1260,8 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
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);
Expand Down
68 changes: 47 additions & 21 deletions lib/SIL/Parser/ParseSIL.cpp
Expand Up @@ -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:
Expand Down
7 changes: 5 additions & 2 deletions lib/Serialization/DeserializeSIL.cpp
Expand Up @@ -1217,12 +1217,15 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn,
Loc, cast<SILBoxType>(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(
Expand Down
5 changes: 3 additions & 2 deletions lib/Serialization/SerializeSIL.cpp
Expand Up @@ -992,8 +992,9 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
}
case SILInstructionKind::AllocStackInst: {
const AllocStackInst *ASI = cast<AllocStackInst>(&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: {
Expand Down
21 changes: 21 additions & 0 deletions test/SIL/Parser/basic.sil
Expand Up @@ -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) () -> () {
Expand Down
46 changes: 34 additions & 12 deletions test/SIL/Serialization/borrow.sil
Expand Up @@ -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):
Expand All @@ -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 : $()
}
25 changes: 25 additions & 0 deletions test/SIL/cloning.sil
Expand Up @@ -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 : $()
}

0 comments on commit b57b222

Please sign in to comment.