Skip to content

Commit ee68d4e

Browse files
Merge pull request swiftlang#14924 from aschwaighofer/wip_effects_release_none
Add an effects(releasenone) function effects attribute
2 parents e6b8930 + d13cc69 commit ee68d4e

File tree

15 files changed

+113
-8
lines changed

15 files changed

+113
-8
lines changed

docs/HighLevelSILOptimizations.rst

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,49 @@ readonly
341341
eliminated, but cannot be reordered or folded in a way that would
342342
move calls to the readnone function across side effects.
343343

344+
releasenone
345+
346+
function has side effects, it can read or write global state, or state
347+
reachable from its arguments. It can however be assumed that no externally
348+
visible release has happened (i.e it is allowed for a ``releasenone``
349+
function to allocate and destruct an object in its implementation as long as
350+
this is does not cause an release of an object visible outside of the
351+
implementation). Here are some examples::
352+
353+
class SomeObject {
354+
final var x: Int = 3
355+
}
356+
357+
var global = SomeObject()
358+
359+
class SomeOtherObject {
360+
var x: Int = 2
361+
deinit {
362+
global = SomeObject()
363+
}
364+
}
365+
366+
@effects(releasenone)
367+
func validReleaseNoneFunction(x: Int) -> Int {
368+
global.x = 5
369+
return x + 2
370+
}
371+
372+
@effects(releasenone)
373+
func validReleaseNoneFunction(x: Int) -> Int {
374+
var notExternallyVisibleObject = SomeObject()
375+
return x + notExternallyVisibleObject.x
376+
}
377+
378+
func notAReleaseNoneFunction(x: Int, y: SomeObject) -> Int {
379+
return x + y.x
380+
}
381+
382+
func notAReleaseNoneFunction(x: Int) -> Int {
383+
var releaseExternallyVisible = SomeOtherObject()
384+
return x + releaseExternallyVisible.x
385+
}
386+
344387
readwrite
345388

346389
function has side effects and the optimizer can't assume anything.

include/swift/AST/AttrKind.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ enum class InlineKind : uint8_t {
7373
enum class EffectsKind : uint8_t {
7474
ReadNone,
7575
ReadOnly,
76+
ReleaseNone,
7677
ReadWrite,
7778
Unspecified
7879
};

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const uint16_t VERSION_MAJOR = 0;
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
5757
/// Don't worry about adhering to the 80-column limit for this line.
58-
const uint16_t VERSION_MINOR = 401; // Last change: ValueOwnership
58+
const uint16_t VERSION_MINOR = 402; // Last change: effects(releasenone)
5959

6060
using DeclIDField = BCFixed<31>;
6161

lib/AST/Attr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,8 @@ StringRef DeclAttribute::getAttrName() const {
601601
return "effects(readnone)";
602602
case EffectsKind::ReadOnly:
603603
return "effects(readonly)";
604+
case EffectsKind::ReleaseNone:
605+
return "effects(releasenone)";
604606
case EffectsKind::ReadWrite:
605607
return "effects(readwrite)";
606608
case EffectsKind::Unspecified:

lib/Parse/ParseDecl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
583583
kind = EffectsKind::ReadNone;
584584
else if (Tok.getText() == "readwrite")
585585
kind = EffectsKind::ReadWrite;
586+
else if (Tok.getText() == "releasenone")
587+
kind = EffectsKind::ReleaseNone;
586588
else {
587589
diagnose(Loc, diag::effects_attribute_unknown_option,
588590
Tok.getText(), AttrName);

lib/ParseSIL/ParseSIL.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,8 @@ static bool parseDeclSILOptional(bool *isTransparent,
936936
*MRK = EffectsKind::ReadOnly;
937937
else if (MRK && SP.P.Tok.getText() == "readwrite")
938938
*MRK = EffectsKind::ReadWrite;
939+
else if (MRK && SP.P.Tok.getText() == "releasenone")
940+
*MRK = EffectsKind::ReleaseNone;
939941
else if (Semantics && SP.P.Tok.getText() == "_semantics") {
940942
SP.P.consumeToken(tok::identifier);
941943
if (SP.P.Tok.getKind() != tok::string_literal) {

lib/SIL/SILPrinter.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2307,8 +2307,10 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
23072307
OS << "[readonly] ";
23082308
else if (getEffectsKind() == EffectsKind::ReadNone)
23092309
OS << "[readnone] ";
2310-
if (getEffectsKind() == EffectsKind::ReadWrite)
2310+
else if (getEffectsKind() == EffectsKind::ReadWrite)
23112311
OS << "[readwrite] ";
2312+
else if (getEffectsKind() == EffectsKind::ReleaseNone)
2313+
OS << "[releasenone] ";
23122314

23132315
for (auto &Attr : getSemanticsAttrs())
23142316
OS << "[_semantics \"" << Attr << "\"] ";

lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ bool SideEffectAnalysis::getDefinedEffects(FunctionEffects &Effects,
159159
return true;
160160
}
161161
switch (F->getEffectsKind()) {
162+
case EffectsKind::ReleaseNone:
163+
Effects.GlobalEffects.Reads = true;
164+
Effects.GlobalEffects.Writes = true;
165+
Effects.GlobalEffects.Releases = false;
166+
return true;
162167
case EffectsKind::ReadNone:
163168
return true;
164169
case EffectsKind::ReadOnly:

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,7 +1317,7 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) {
13171317

13181318
// Optimize readonly functions with no meaningful users.
13191319
SILFunction *SF = AI->getReferencedFunction();
1320-
if (SF && SF->getEffectsKind() < EffectsKind::ReadWrite) {
1320+
if (SF && SF->getEffectsKind() < EffectsKind::ReleaseNone) {
13211321
UserListTy Users;
13221322
if (recursivelyCollectARCUsers(Users, AI)) {
13231323
if (eraseApply(AI, Users))
@@ -1327,7 +1327,7 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) {
13271327
}
13281328

13291329
if (SF) {
1330-
if (SF->getEffectsKind() < EffectsKind::ReadWrite) {
1330+
if (SF->getEffectsKind() < EffectsKind::ReleaseNone) {
13311331
// Try to optimize string concatenation.
13321332
if (auto I = optimizeConcatenationOfStringLiterals(AI)) {
13331333
return I;
@@ -1450,7 +1450,7 @@ SILInstruction *SILCombiner::visitTryApplyInst(TryApplyInst *AI) {
14501450

14511451
// Optimize readonly functions with no meaningful users.
14521452
SILFunction *Fn = AI->getReferencedFunction();
1453-
if (Fn && Fn->getEffectsKind() < EffectsKind::ReadWrite) {
1453+
if (Fn && Fn->getEffectsKind() < EffectsKind::ReleaseNone) {
14541454
UserListTy Users;
14551455
if (isTryApplyResultNotUsed(Users, AI)) {
14561456
SILBasicBlock *BB = AI->getParent();

lib/SILOptimizer/Utils/Local.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -693,8 +693,8 @@ bool StringConcatenationOptimizer::extractStringConcatOperands() {
693693
auto *FRILeftFun = FRILeft->getReferencedFunction();
694694
auto *FRIRightFun = FRIRight->getReferencedFunction();
695695

696-
if (FRILeftFun->getEffectsKind() >= EffectsKind::ReadWrite ||
697-
FRIRightFun->getEffectsKind() >= EffectsKind::ReadWrite)
696+
if (FRILeftFun->getEffectsKind() >= EffectsKind::ReleaseNone ||
697+
FRIRightFun->getEffectsKind() >= EffectsKind::ReleaseNone)
698698
return false;
699699

700700
if (!FRILeftFun->hasSemanticsAttrs() || !FRIRightFun->hasSemanticsAttrs())

0 commit comments

Comments
 (0)