Skip to content

[SIL] Added lexical flag to alloc_stack. #39291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/SIL.rst
Original file line number Diff line number Diff line change
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alloc_stack does not have an operand.

How is lexical different to debug-var-attr? Doesn't alloc_stack correspond to a variable if debug-var-attr is present?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Fixed at #39484 .

Is the debug-var-attr always present? If it is, @atrick may know the answer. At worst, this might be duplicative and we'll need to tweak the change to Mem2Reg.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a general rule, debug information is not supposed to affect semantics. If we decide to reuse the same information for both purposes, then we need to specify in the class and API that it is not simply for debug information, audit, and verify the implementation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still, I would prefer to not have redundant information in the instruction attributes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adrian-prantl do you know in which cases debug-var-attr is present?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debug-var-attr is present iff the alloc_stack is the only storage location for a (part of a) source variable. It is there regardless of whether -g is on or not. There can be alloc_stack instructions that don't have a debug-var-attr, in purely compiler-generated code (thunks, etc).

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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 : $()
}