Skip to content

Commit 8e2e7a7

Browse files
committed
SIL: make argument effects more readable in textual SIL
So far, argument effects were printed in square brackets before the function name, e.g. ``` sil [escapes !%0.**, !%1, %1.c*.v** => %0.v**] @foo : $@convention(thin) (@guaranteed T) -> @out S { bb0(%0 : $*S, %1 : @guaranteed $T): ... ``` As we are adding more argument effects, this becomes unreadable. To make it more readable, print the effects after the opening curly brace, and print a separate line for each argument. E.g. ``` sil [ossa] @foo : $@convention(thin) (@guaranteed T) -> @out S { [%0: noescape **] [%1: noescape, escape c*.v** => %0.v**] bb0(%0 : $*S, %1 : @guaranteed $T): ... ```
1 parent e1ff0aa commit 8e2e7a7

23 files changed

+520
-335
lines changed

SwiftCompilerSources/Sources/SIL/Effects.swift

Lines changed: 79 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,22 @@ public struct ArgumentEffect : CustomStringConvertible, CustomReflectable {
1616
public typealias Path = SmallProjectionPath
1717

1818
public enum Kind {
19-
/// The selected argument value does not escape.
19+
/// The argument value does not escape.
2020
///
2121
/// Syntax examples:
22-
/// !%0 // argument 0 does not escape
23-
/// !%0.** // argument 0 and all transitively contained values do not escape
22+
/// [%0: noescape] // argument 0 does not escape
23+
/// [%0: noescape **] // argument 0 and all transitively contained values do not escape
2424
///
2525
case notEscaping
2626

27-
/// The selected argument value escapes to the specified selection (= first payload).
27+
/// The argument value escapes to the function return value.
2828
///
2929
/// Syntax examples:
30-
/// %0.s1 => %r // field 2 of argument 0 exclusively escapes via return.
31-
/// %0.s1 -> %1 // field 2 of argument 0 - and other values - escape to argument 1.
30+
/// [%0: escape s1 => %r] // field 2 of argument 0 exclusively escapes via return.
31+
/// [%0: escape s1 -> %r] // field 2 of argument 0 - and other values - escape via return
3232
///
33-
/// The "exclusive" flag (= second payload) is true if only the selected argument escapes
34-
/// to the specified selection, but nothing else escapes to it.
33+
/// The "exclusive" flag (= second payload) is true if only the argument escapes,
34+
/// but nothing else escapes to the return value.
3535
/// For example, "exclusive" is true for the following function:
3636
///
3737
/// @_effect(escaping c => return)
@@ -46,6 +46,14 @@ public struct ArgumentEffect : CustomStringConvertible, CustomReflectable {
4646
///
4747
case escapingToReturn(Path, Bool) // toPath, exclusive
4848

49+
/// Like `escapingToReturn`, but the argument escapes to another argument.
50+
///
51+
/// Example: The argument effects of
52+
/// func argToArgEscape(_ r: inout Class, _ c: Class) { r = c }
53+
///
54+
/// would be
55+
/// [%1: escape => %0] // Argument 1 escapes to argument 0
56+
///
4957
case escapingToArgument(Int, Path, Bool) // toArgumentIndex, toPath, exclusive
5058
}
5159

@@ -111,21 +119,28 @@ public struct ArgumentEffect : CustomStringConvertible, CustomReflectable {
111119
return argumentIndex == rhsArgIdx && rhsPath.matches(pattern: pathPattern)
112120
}
113121

114-
public var description: String {
115-
let selectedArg = "%\(argumentIndex)" + (pathPattern.isEmpty ? "" : ".\(pathPattern)")
122+
public var headerDescription: String {
123+
"%\(argumentIndex)\(isDerived ? "" : "!"): "
124+
}
116125

126+
public var bodyDescription: String {
127+
let patternStr = pathPattern.isEmpty ? "" : " \(pathPattern)"
117128
switch kind {
118129
case .notEscaping:
119-
return "!\(selectedArg)"
130+
return "noescape\(patternStr)"
120131
case .escapingToReturn(let toPath, let exclusive):
121-
let pathStr = (toPath.isEmpty ? "" : ".\(toPath)")
122-
return "\(selectedArg) \(exclusive ? "=>" : "->") %r\(pathStr)"
132+
let toPathStr = (toPath.isEmpty ? "" : ".\(toPath)")
133+
return "escape\(patternStr) \(exclusive ? "=>" : "->") %r\(toPathStr)"
123134
case .escapingToArgument(let toArgIdx, let toPath, let exclusive):
124-
let pathStr = (toPath.isEmpty ? "" : ".\(toPath)")
125-
return "\(selectedArg) \(exclusive ? "=>" : "->") %\(toArgIdx)\(pathStr)"
135+
let toPathStr = (toPath.isEmpty ? "" : ".\(toPath)")
136+
return "escape\(patternStr) \(exclusive ? "=>" : "->") %\(toArgIdx)\(toPathStr)"
126137
}
127138
}
128139

140+
public var description: String {
141+
headerDescription + bodyDescription
142+
}
143+
129144
public var customMirror: Mirror { Mirror(self, children: []) }
130145
}
131146

@@ -164,8 +179,27 @@ public struct FunctionEffects : CustomStringConvertible, CustomReflectable {
164179
argumentEffects = argumentEffects.filter { !$0.isDerived }
165180
}
166181

182+
public var argumentEffectsDescription: String {
183+
var currentArgIdx = -1
184+
var currentIsDerived = false
185+
var result = ""
186+
for effect in argumentEffects {
187+
if effect.argumentIndex != currentArgIdx || effect.isDerived != currentIsDerived {
188+
if currentArgIdx >= 0 { result += "]\n" }
189+
result += "[\(effect.headerDescription)"
190+
currentArgIdx = effect.argumentIndex
191+
currentIsDerived = effect.isDerived
192+
} else {
193+
result += ", "
194+
}
195+
result += effect.bodyDescription
196+
}
197+
if currentArgIdx >= 0 { result += "]\n" }
198+
return result
199+
}
200+
167201
public var description: String {
168-
return "[" + argumentEffects.map { $0.description }.joined(separator: ", ") + "]"
202+
return argumentEffectsDescription
169203
}
170204

171205
public var customMirror: Mirror { Mirror(self, children: []) }
@@ -237,24 +271,37 @@ extension StringParser {
237271
return ArgumentEffect.Path()
238272
}
239273

240-
mutating func parseEffectFromSIL(for function: Function, isDerived: Bool) throws -> ArgumentEffect {
241-
if consume("!") {
242-
let argIdx = try parseArgumentIndexFromSIL()
243-
let path = try parsePathPatternFromSIL()
244-
return ArgumentEffect(.notEscaping, argumentIndex: argIdx, pathPattern: path, isDerived: isDerived)
274+
mutating func parseEffectsFromSIL(to effects: inout FunctionEffects) throws {
275+
let argumentIndex = try parseArgumentIndexFromSIL()
276+
let isDerived = !consume("!")
277+
if !consume(":") {
278+
try throwError("expected ':'")
245279
}
246-
let fromArgIdx = try parseArgumentIndexFromSIL()
247-
let fromPath = try parsePathPatternFromSIL()
248-
let exclusive = try parseEscapingArrow()
249-
if consume("%r") {
250-
let toPath = try parsePathPatternFromSIL()
251-
return ArgumentEffect(.escapingToReturn(toPath, exclusive),
252-
argumentIndex: fromArgIdx, pathPattern: fromPath, isDerived: isDerived)
280+
repeat {
281+
let effect = try parseEffectFromSIL(argumentIndex: argumentIndex, isDerived: isDerived)
282+
effects.argumentEffects.append(effect)
283+
} while consume(",")
284+
}
285+
286+
mutating func parseEffectFromSIL(argumentIndex: Int, isDerived: Bool) throws -> ArgumentEffect {
287+
if consume("noescape") {
288+
let path = try parseProjectionPathFromSIL()
289+
return ArgumentEffect(.notEscaping, argumentIndex: argumentIndex, pathPattern: path, isDerived: isDerived)
290+
}
291+
if consume("escape") {
292+
let fromPath = try parseProjectionPathFromSIL()
293+
let exclusive = try parseEscapingArrow()
294+
if consume("%r") {
295+
let toPath = consume(".") ? try parseProjectionPathFromSIL() : ArgumentEffect.Path()
296+
return ArgumentEffect(.escapingToReturn(toPath, exclusive),
297+
argumentIndex: argumentIndex, pathPattern: fromPath, isDerived: isDerived)
298+
}
299+
let toArgIdx = try parseArgumentIndexFromSIL()
300+
let toPath = consume(".") ? try parseProjectionPathFromSIL() : ArgumentEffect.Path()
301+
return ArgumentEffect(.escapingToArgument(toArgIdx, toPath, exclusive),
302+
argumentIndex: argumentIndex, pathPattern: fromPath, isDerived: isDerived)
253303
}
254-
let toArgIdx = try parseArgumentIndexFromSIL()
255-
let toPath = try parsePathPatternFromSIL()
256-
return ArgumentEffect(.escapingToArgument(toArgIdx, toPath, exclusive),
257-
argumentIndex: fromArgIdx, pathPattern: fromPath, isDerived: isDerived)
304+
try throwError("unknown effect")
258305
}
259306

260307
mutating func parseArgumentIndexFromSIL() throws -> Int {
@@ -267,13 +314,6 @@ extension StringParser {
267314
try throwError("expected parameter")
268315
}
269316

270-
mutating func parsePathPatternFromSIL() throws -> ArgumentEffect.Path {
271-
if consume(".") {
272-
return try parseProjectionPathFromSIL()
273-
}
274-
return ArgumentEffect.Path()
275-
}
276-
277317
private mutating func parseEscapingArrow() throws -> Bool {
278318
if consume("=>") { return true }
279319
if consume("->") { return false }

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -133,27 +133,36 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
133133
},
134134
// writeFn
135135
{ (f: BridgedFunction, os: BridgedOStream, idx: Int) in
136-
let s = f.function.effects.argumentEffects[idx].description
136+
let s: String
137+
if idx >= 0 {
138+
s = f.function.effects.argumentEffects[idx].bodyDescription
139+
} else {
140+
s = f.function.effects.argumentEffectsDescription
141+
}
137142
s._withStringRef { OStream_write(os, $0) }
138143
},
139144
// parseFn:
140-
{ (f: BridgedFunction, str: llvm.StringRef, fromSIL: Int, isDerived: Int, paramNames: BridgedArrayRef) -> BridgedParsingError in
145+
{ (f: BridgedFunction, str: llvm.StringRef, fromSIL: Int, argumentIndex: Int, isDerived: Int, paramNames: BridgedArrayRef) -> BridgedParsingError in
141146
do {
142147
var parser = StringParser(str.string)
143-
let effect: ArgumentEffect
144-
if fromSIL != 0 {
145-
effect = try parser.parseEffectFromSIL(for: f.function, isDerived: isDerived != 0)
148+
149+
if fromSIL != 0 && argumentIndex < 0 {
150+
try parser.parseEffectsFromSIL(to: &f.function.effects)
146151
} else {
147-
let paramToIdx = paramNames.withElements(ofType: llvm.StringRef.self) {
148-
(buffer: UnsafeBufferPointer<llvm.StringRef>) -> Dictionary<String, Int> in
149-
let keyValPairs = buffer.enumerated().lazy.map { ($0.1.string, $0.0) }
150-
return Dictionary(uniqueKeysWithValues: keyValPairs)
152+
let effect: ArgumentEffect
153+
if fromSIL != 0 {
154+
effect = try parser.parseEffectFromSIL(argumentIndex: argumentIndex, isDerived: isDerived != 0)
155+
} else {
156+
let paramToIdx = paramNames.withElements(ofType: llvm.StringRef.self) {
157+
(buffer: UnsafeBufferPointer<llvm.StringRef>) -> Dictionary<String, Int> in
158+
let keyValPairs = buffer.enumerated().lazy.map { ($0.1.string, $0.0) }
159+
return Dictionary(uniqueKeysWithValues: keyValPairs)
160+
}
161+
effect = try parser.parseEffectFromSource(for: f.function, params: paramToIdx)
151162
}
152-
effect = try parser.parseEffectFromSource(for: f.function, params: paramToIdx)
163+
f.function.effects.argumentEffects.append(effect)
153164
}
154165
if !parser.isEmpty() { try parser.throwError("syntax error") }
155-
156-
f.function.effects.argumentEffects.append(effect)
157166
} catch let error as ParsingError {
158167
return BridgedParsingError(message: error.message.utf8Start, position: error.position)
159168
} catch {
@@ -179,20 +188,14 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
179188
resultArgDelta: destResultArgs - srcResultArgs)
180189
return 1
181190
},
182-
// getEffectFlags
183-
{ (f: BridgedFunction, idx: Int) -> Int in
191+
// getEffectInfo
192+
{ (f: BridgedFunction, idx: Int) -> BridgedEffectInfo in
184193
let argEffects = f.function.effects.argumentEffects
185-
if idx >= argEffects.count { return 0 }
186-
let effect = argEffects[idx]
187-
var flags = 0
188-
switch effect.kind {
189-
case .notEscaping, .escapingToArgument, .escapingToReturn:
190-
flags |= Int(EffectsFlagEscape)
191-
}
192-
if effect.isDerived {
193-
flags |= Int(EffectsFlagDerived)
194+
if idx >= argEffects.count {
195+
return BridgedEffectInfo(argumentIndex: -1, isDerived: false)
194196
}
195-
return flags
197+
let effect = argEffects[idx]
198+
return BridgedEffectInfo(argumentIndex: effect.argumentIndex, isDerived: effect.isDerived)
196199
}
197200
)
198201
}

docs/SIL.rst

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ Functions
956956
decl ::= sil-function
957957
sil-function ::= 'sil' sil-linkage? sil-function-attribute+
958958
sil-function-name ':' sil-type
959-
'{' sil-basic-block+ '}'
959+
'{' argument-effect* sil-basic-block* '}'
960960
sil-function-name ::= '@' [A-Za-z_0-9]+
961961

962962
SIL functions are defined with the ``sil`` keyword. SIL function names
@@ -966,6 +966,8 @@ and is usually the mangled name of the originating Swift declaration.
966966
The ``sil`` syntax declares the function's name and SIL type, and
967967
defines the body of the function inside braces. The declared type must
968968
be a function type, which may be generic.
969+
If there are no `sil-basic-block`s contained in the body, the function
970+
is an external declaration.
969971

970972

971973
Function Attributes
@@ -1094,30 +1096,6 @@ from the command line.
10941096
sil-function-effects ::= 'releasenone'
10951097

10961098
The specified memory effects of the function.
1097-
::
1098-
1099-
sil-function-attribute ::= '[' 'escapes' escape-list ']'
1100-
sil-function-attribute ::= '[' 'defined_escapes' escape-list ']'
1101-
escape-list ::= (escape-list ',')? escape
1102-
escape ::= '!' arg-selection // not-escaping
1103-
escape ::= arg-selection '=>' arg-selection // exclusive escaping
1104-
escape ::= arg-selection '->' arg-selection // not-exclusive escaping
1105-
arg-selection ::= arg-or-return ('.' projection-path)?
1106-
arg-or-return ::= '%' [0-9]+
1107-
arg-or-return ::= '%r'
1108-
projection-path ::= (projection-path '.')? path-component
1109-
path-component ::= 's' [0-9]+ // struct field
1110-
path-component ::= 'c' [0-9]+ // class field
1111-
path-component ::= 'ct' // class tail element
1112-
path-component ::= 'e' [0-9]+ // enum case
1113-
path-component ::= [0-9]+ // tuple element
1114-
path-component ::= 'v**' // any value fields
1115-
path-component ::= 'c*' // any class field
1116-
path-component ::= '**' // anything
1117-
1118-
The escaping effects for function arguments. For details see the documentation
1119-
in ``SwiftCompilerSources/Sources/SIL/Effects.swift``.
1120-
11211099
::
11221100

11231101
sil-function-attribute ::= '[_semantics "' [A-Za-z._0-9]+ '"]'
@@ -1147,6 +1125,34 @@ Specifies the performance constraints for the function, which defines which type
11471125
of runtime functions are allowed to be called from the function.
11481126

11491127

1128+
Argument Effects
1129+
````````````````
1130+
1131+
The effects for function arguments. For details see the documentation
1132+
in ``SwiftCompilerSources/Sources/SIL/Effects.swift``.
1133+
::
1134+
1135+
argument-effect ::= '[' argument-name defined-effect? ':' effect (',' effect)*]'
1136+
argument-name ::= '%' [0-9]+
1137+
defined-effect ::= '!' // the effect is defined in the source code and not
1138+
// derived by the optimizer
1139+
1140+
effect ::= 'noescape' projection-path?
1141+
effect ::= 'escape' projection-path? '=>' arg-or-return // exclusive escape
1142+
effect ::= 'escape' projection-path? '->' arg-or-return // not-exclusive escape
1143+
arg-or-return ::= argument-name ('.' projection-path)?
1144+
arg-or-return ::= '%r' ('.' projection-path)?
1145+
1146+
projection-path ::= path-component ('.' path-component)*
1147+
path-component ::= 's' [0-9]+ // struct field
1148+
path-component ::= 'c' [0-9]+ // class field
1149+
path-component ::= 'ct' // class tail element
1150+
path-component ::= 'e' [0-9]+ // enum case
1151+
path-component ::= [0-9]+ // tuple element
1152+
path-component ::= 'v**' // any value fields
1153+
path-component ::= 'c*' // any class field
1154+
path-component ::= '**' // anything
1155+
11501156
Basic Blocks
11511157
~~~~~~~~~~~~
11521158
::

include/swift/SIL/SILBridging.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,9 @@ typedef enum {
217217
#include "swift/AST/Builtins.def"
218218
} BridgedBuiltinID;
219219

220-
enum {
221-
EffectsFlagEscape = 0x1,
222-
EffectsFlagDerived = 0x2
220+
struct BridgedEffectInfo {
221+
SwiftInt argumentIndex;
222+
bool isDerived;
223223
};
224224

225225
void registerBridgedClass(llvm::StringRef className, SwiftMetatype metatype);
@@ -231,17 +231,17 @@ typedef void (* _Nonnull FunctionWriteFn)(BridgedFunction,
231231
BridgedOStream, SwiftInt);
232232
typedef BridgedParsingError (*_Nonnull FunctionParseFn)(BridgedFunction,
233233
llvm::StringRef,
234-
SwiftInt, SwiftInt,
234+
SwiftInt, SwiftInt, SwiftInt,
235235
BridgedArrayRef);
236236
typedef SwiftInt (* _Nonnull FunctionCopyEffectsFn)(BridgedFunction,
237237
BridgedFunction);
238-
typedef SwiftInt (* _Nonnull FunctionGetEffectFlagsFn)(BridgedFunction, SwiftInt);
238+
typedef BridgedEffectInfo (* _Nonnull FunctionGetEffectInfoFn)(BridgedFunction, SwiftInt);
239239

240240
void Function_register(SwiftMetatype metatype,
241241
FunctionRegisterFn initFn, FunctionRegisterFn destroyFn,
242242
FunctionWriteFn writeFn, FunctionParseFn parseFn,
243243
FunctionCopyEffectsFn copyEffectsFn,
244-
FunctionGetEffectFlagsFn hasEffectsFn);
244+
FunctionGetEffectInfoFn effectInfoFn);
245245

246246
SwiftInt PassContext_continueWithNextSubpassRun(BridgedPassContext passContext,
247247
OptionalBridgedInstruction inst);

include/swift/SIL/SILFunction.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,18 +1031,16 @@ class SILFunction
10311031
EffectsKindAttr = unsigned(E);
10321032
}
10331033

1034-
enum class ArgEffectKind {
1035-
Unknown,
1036-
Escape
1037-
};
1038-
10391034
std::pair<const char *, int> parseEffects(StringRef attrs, bool fromSIL,
1040-
bool isDerived,
1035+
int argumentIndex, bool isDerived,
10411036
ArrayRef<StringRef> paramNames);
10421037
void writeEffect(llvm::raw_ostream &OS, int effectIdx) const;
1038+
void writeEffects(llvm::raw_ostream &OS) const {
1039+
writeEffect(OS, -1);
1040+
}
10431041
void copyEffects(SILFunction *from);
10441042
bool hasArgumentEffects() const;
1045-
void visitArgEffects(std::function<void(int, bool, ArgEffectKind)> c) const;
1043+
void visitArgEffects(std::function<void(int, int, bool)> c) const;
10461044

10471045
Purpose getSpecialPurpose() const { return specialPurpose; }
10481046

0 commit comments

Comments
 (0)