Skip to content

Commit 401be14

Browse files
authored
Merge pull request #18119 from atrick/nfc-clarify-arg-index
[NFC] SILFunctionArgument and ApplySite docs and comments.
2 parents 40a29b8 + 4a283be commit 401be14

File tree

3 files changed

+251
-49
lines changed

3 files changed

+251
-49
lines changed

docs/SIL.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ In contrast to LLVM IR, SIL is a generally target-independent format
2828
representation that can be used for code distribution, but it can also express
2929
target-specific concepts as well as LLVM can.
3030

31+
For more information on developing the implementation of SIL and SIL passes, see
32+
SILProgrammersManual.md.
33+
3134
SIL in the Swift Compiler
3235
-------------------------
3336

docs/SILProgrammersManual.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# SIL Programmers' Manual
2+
3+
This document provides information for developers working on the
4+
implementation of SIL. The formal specification of the Swift
5+
Intermediate _Language_ is in SIL.rst. This is a guide to the internal
6+
implementation. Source comments normally provide this level of
7+
information as much as possible. However, some features of the
8+
implementation are spread out across the source base. This
9+
documentation is meant to offer a central point for approaching the
10+
source base with links to the code for more detailed comments.
11+
12+
## SILType
13+
14+
TBD: Define the different levels of types. Explain type lowering with
15+
examples.
16+
17+
## SILInstructionResults
18+
19+
TBD: Explain how the various types fit together with pointers to the
20+
source: SILValue, SILInstruction, SingleValueInstruction,
21+
MultipleValueInstructionResult. And why it was done this way.
22+
23+
## `SILFunction` and `apply` arguments.
24+
25+
Throughout the compiler, integer indices are used to identify argument
26+
positions in several different contexts:
27+
28+
- A `SILFunctionType` has a tuple of parameters.
29+
30+
- The SIL function definition has a list of `SILFunctionArgument`.
31+
This is the callee-side argument list. It includes indirect results.
32+
33+
- `apply`, `try_apply`, and `begin_apply` have "applied arguments":
34+
the subset of instruction operands representing the callee's
35+
`SILFunctionArgument` list.
36+
37+
- `partial_apply` has "applied arguments": the subset of instruction
38+
operands representing closure captures. Closure captures in turn map
39+
to a subset of the callee's `SILFunctionArgument` list.
40+
41+
- In the above three contexts, `SILFunctionArgument`, `apply`, and
42+
`partial_apply`, the argument indices also depend on the SIL stage:
43+
Canonical vs. Lowered.
44+
45+
Consider the example:
46+
47+
```
48+
func example<T>(Int, i, t: T) -> (Int, T) {
49+
let foo = { return ($0, t) }
50+
return foo(i)
51+
}
52+
```
53+
54+
The closure `foo` has the indices as numbered below in each
55+
context, ignoring the calling convention and boxing/unboxing of
56+
captures for brevity:
57+
58+
The closure's `SILFunctionType` has two direct formal parameters at
59+
indices (`#0`, `#1`) and one direct formal result of tuple type:
60+
61+
```
62+
SILFunctionType(foo): (#0: Int, #1: T) -> @out (Int, T)
63+
```
64+
65+
Canonical SIL with opaque values matches `SILFunctionType`. The
66+
definition of `foo` has two direct `SILFunctionArgument`s at (`#0`,
67+
`#1`):
68+
69+
```
70+
SILFunctionArguments: (#0: Int, #1: T) -> (Int, T)
71+
```
72+
73+
The Lowered SIL for `foo`s definition has an indirect "result
74+
argument" at index #0. The function parameter indices are now (`#1`,
75+
`#2`):
76+
77+
```
78+
SILFunctionArguments: (#0: *T, #1: Int, #2: T) -> Int
79+
```
80+
81+
Creation of the closure has one applied argument at index `#0`. Note
82+
that the first applied argument is actually the second operand (the
83+
first is the callee), and in lowered SIL, it is actually the third
84+
SILFunctionArgument (after the indirect result and first parameter):
85+
86+
```
87+
%closure = partial_apply @foo(#0: %t)
88+
```
89+
90+
Application of the closure with opaque values has one applied
91+
argument:
92+
93+
```
94+
%resultTuple = apply %closure(#0: i)
95+
```
96+
97+
Lowered application of the closure has two applied arguments:
98+
99+
```
100+
%directResult = apply %closure(#0: %indirectResult: $*T, #1: i)
101+
```
102+
103+
The mapping between `SILFunctionType` and `SILFunctionArgument`, which depends
104+
on the SIL stage, is managed by the
105+
[SILFunctionConventions](https://github.com/apple/swift/blob/master/include/swift/SIL/SILFunctionConventions.h)
106+
abstraction. This API follows naming conventions to communicate the meaning of the integer indices:
107+
108+
- "Parameters" refer to the function signature's tuple of arguments.
109+
110+
- "SILArguments" refer to the set of `SILFunctionArgument` in the callee's entry block, including any indirect results required by the current SIL stage.
111+
112+
These argument indices and their relative offsets should never be
113+
hardcoded. Although this is common practice in LLVM, it should be
114+
avoided in SIL: (a) the structure of SIL instructions, particularly
115+
applies, is much more nuanced than LLVM IR, (b) assumptions that may
116+
be valid at the initial point of use are often copied into parts of
117+
the code where they are no longer valid; and (c) unlike LLVM IR, SIL
118+
is not stable and will continue evolving.
119+
120+
Translation between SILArgument and parameter indices should use:
121+
`SILFunctionConventions::getSILArgIndexOfFirstParam()`.
122+
123+
Translation between SILArgument and result indices should use:
124+
`SILFunctionConventions::getSILArgIndexOfFirstIndirectResult()`.
125+
126+
Convenience methods exist for the most common uses, so there is
127+
typically no need to use the above "IndexOfFirst" methods to translate
128+
one integer index into another. The naming convention of the
129+
convenience method should clearly indicate which form of index it
130+
expects. For example, information about a parameter's type can be retrieved directly from a SILArgument index: `getParamInfoForSILArg(index)`, `getSILArgumentConvention(index)`, and `getSILArgumentType(index)`.
131+
132+
Another abstraction,
133+
[`ApplySite`](https://github.com/search?utf8=✓&q=%22class+ApplySite%22+repo%3Aapple%2Fswift+path%3Ainclude%2Fswift%2FSIL&type=Code&ref=advsearch&l=&l=),
134+
abstracts over the various kinds of `apply` instructions, including
135+
`try_apply`, `begin_apply`, and `partial_apply`.
136+
137+
`ApplySite::getSubstCalleeConv()` is commonly used to query the
138+
callee's `SILFunctionConventions` which provides information about the
139+
function's type and its definition as explained above. Information about the applied arguments can be queried directly from the `ApplySite` API.
140+
141+
For example, `ApplySite::getAppliedArgumentConvention(index)` takes an
142+
applied argument index, while
143+
`SILFunctionArguments::getSILArgumentConvention(index`) takes a
144+
`SILFunctionArgument` index. They both return the same information,
145+
but from a different viewpoint.
146+
147+
A common mistake is to directly map the ApplySite's caller-side
148+
arguments onto callee-side SILFunctionArguments. This happens to work
149+
until the same code is exposed to a `partial_apply`. Instead, use the `ApplySite` API for applied argument indices, or use
150+
`ApplySite::getCalleeArgIndexOfFirstAppliedArg()` to translate the
151+
apply's arguments into function convention arguments.
152+
153+
Consistent use of common idioms for accessing arguments should be
154+
adopted throughout the compiler. Plenty of bugs have resulted from
155+
assumptions about the form of SIL made in one area of the compiler
156+
that have been copied into other parts of the compiler. For example,
157+
knowing that a block of code is guarded by a dynamic condition that
158+
rules out PartialApplies is no excuse to conflate applied arguments
159+
with function arguments. Also, without consistent use of common
160+
idioms, it becomes overly burdensome to evolve these APIs over time.
161+
162+
## SILGen
163+
164+
TBD: Possibly link to a separate document explaining the architecture of SILGen.
165+
166+
Some information from SIL.rst could be moved here.
167+
168+
## IRGen
169+
170+
TBD: Possibly link to a separate document explaining the architecture of IRGen.
171+
172+
## SILAnalysis and the PassManager
173+
174+
TBD: describe the mechanism by which passes invalidate and update the
175+
PassManager and its avaiable analyses.
176+
177+
## High Level SIL Optimizations
178+
179+
HighLevelSILOptimizations.rst discusses how the optimizer imbues
180+
certain special SIL types and SIL functions with higher level
181+
semantics.

include/swift/SIL/SILInstruction.h

Lines changed: 67 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1936,6 +1936,7 @@ class ApplyInstBase<Impl, Base, true>
19361936
return OperandValueArrayRef(opsWithoutSelf);
19371937
}
19381938

1939+
/// Return the SILArgumentConvention for the given applied argument index.
19391940
SILArgumentConvention getArgumentConvention(unsigned index) const {
19401941
return getSubstCalleeConv().getSILArgumentConvention(index);
19411942
}
@@ -7620,47 +7621,59 @@ class ApplySite {
76207621
FOREACH_IMPL_RETURN(getSubstitutionMap());
76217622
}
76227623

7623-
/// The arguments passed to this instruction.
7624+
/// Return the associated specialization information.
7625+
const GenericSpecializationInformation *getSpecializationInfo() const {
7626+
FOREACH_IMPL_RETURN(getSpecializationInfo());
7627+
}
7628+
7629+
/// Return an operand list corresponding to the applied arguments.
76247630
MutableArrayRef<Operand> getArgumentOperands() const {
76257631
FOREACH_IMPL_RETURN(getArgumentOperands());
76267632
}
76277633

7628-
/// The arguments passed to this instruction.
7634+
/// Return a list of applied argument values.
76297635
OperandValueArrayRef getArguments() const {
76307636
FOREACH_IMPL_RETURN(getArguments());
76317637
}
76327638

7633-
/// The number of call arguments.
7639+
/// Return the number of applied arguments.
76347640
unsigned getNumArguments() const {
76357641
FOREACH_IMPL_RETURN(getNumArguments());
76367642
}
76377643

7638-
unsigned getOperandIndexOfFirstArgument() {
7639-
FOREACH_IMPL_RETURN(getArgumentOperandNumber());
7644+
/// Return the apply operand for the given applied argument index.
7645+
Operand &getArgumentRef(unsigned i) const { return getArgumentOperands()[i]; }
7646+
7647+
/// Return the ith applied argument.
7648+
SILValue getArgument(unsigned i) const { return getArguments()[i]; }
7649+
7650+
/// Set the ith applied argument.
7651+
void setArgument(unsigned i, SILValue V) const {
7652+
getArgumentOperands()[i].set(V);
76407653
}
76417654

7642-
/// Return the associated specialization information.
7643-
const GenericSpecializationInformation *getSpecializationInfo() const {
7644-
FOREACH_IMPL_RETURN(getSpecializationInfo());
7655+
/// Return the operand index of the first applied argument.
7656+
unsigned getOperandIndexOfFirstArgument() const {
7657+
FOREACH_IMPL_RETURN(getArgumentOperandNumber());
76457658
}
76467659
#undef FOREACH_IMPL_RETURN
76477660

7648-
/// The arguments passed to this instruction, without self.
7649-
OperandValueArrayRef getArgumentsWithoutSelf() const {
7650-
switch (Inst->getKind()) {
7651-
case SILInstructionKind::ApplyInst:
7652-
return cast<ApplyInst>(Inst)->getArgumentsWithoutSelf();
7653-
case SILInstructionKind::BeginApplyInst:
7654-
return cast<BeginApplyInst>(Inst)->getArgumentsWithoutSelf();
7655-
case SILInstructionKind::TryApplyInst:
7656-
return cast<TryApplyInst>(Inst)->getArgumentsWithoutSelf();
7657-
default:
7658-
llvm_unreachable("not implemented for this instruction!");
7659-
}
7661+
/// Returns true if \p oper is an argument operand and not the callee
7662+
/// operand.
7663+
bool isArgumentOperand(const Operand &oper) const {
7664+
return oper.getOperandNumber() >= getOperandIndexOfFirstArgument();
7665+
}
7666+
7667+
/// Return the applied argument index for the given operand.
7668+
unsigned getArgumentIndex(const Operand &oper) const {
7669+
assert(oper.getUser() == Inst);
7670+
assert(isArgumentOperand(oper));
7671+
7672+
return oper.getOperandNumber() - getOperandIndexOfFirstArgument();
76607673
}
76617674

7662-
// Get the callee argument index corresponding to the caller's first applied
7663-
// argument. Returns 0 for full applies. May return > 0 for partial applies.
7675+
/// Return the callee's function argument index corresponding to the first
7676+
/// applied argument: 0 for full applies; >= 0 for partial applies.
76647677
unsigned getCalleeArgIndexOfFirstAppliedArg() const {
76657678
switch (Inst->getKind()) {
76667679
case SILInstructionKind::ApplyInst:
@@ -7683,35 +7696,30 @@ class ApplySite {
76837696
}
76847697
}
76857698

7686-
/// Returns true if \p oper is an argument operand and not the callee
7687-
/// operand.
7688-
bool isArgumentOperand(const Operand &oper) {
7689-
return oper.getOperandNumber() >= getOperandIndexOfFirstArgument();
7699+
/// Return the callee's function argument index corresponding to the given
7700+
/// apply operand. Each function argument index identifies a
7701+
/// SILFunctionArgument in the callee and can be used as a
7702+
/// SILFunctionConvention argument index.
7703+
///
7704+
/// Note: Passing an applied argument index into SILFunctionConvention, as
7705+
/// opposed to a function argument index, is incorrect.
7706+
unsigned getCalleeArgIndex(const Operand &oper) const {
7707+
return getCalleeArgIndexOfFirstAppliedArg() + getArgumentIndex(oper);
76907708
}
76917709

7692-
// Translate the index of the argument to the full apply or partial_apply into
7693-
// to the corresponding index into the arguments of the called function.
7694-
unsigned getCalleeArgIndex(const Operand &oper) {
7695-
assert(oper.getUser() == Inst);
7696-
assert(isArgumentOperand(oper));
7697-
7698-
unsigned appliedArgIdx =
7699-
oper.getOperandNumber() - getOperandIndexOfFirstArgument();
7700-
7701-
return getCalleeArgIndexOfFirstAppliedArg() + appliedArgIdx;
7710+
/// Return the SILArgumentConvention for the given applied argument operand.
7711+
SILArgumentConvention getArgumentConvention(Operand &oper) const {
7712+
unsigned calleeArgIdx =
7713+
getCalleeArgIndexOfFirstAppliedArg() + getArgumentIndex(oper);
7714+
return getSubstCalleeConv().getSILArgumentConvention(calleeArgIdx);
77027715
}
77037716

7704-
Operand &getArgumentRef(unsigned i) const { return getArgumentOperands()[i]; }
7705-
7706-
/// Return the ith argument passed to this instruction.
7707-
SILValue getArgument(unsigned i) const { return getArguments()[i]; }
7708-
7709-
/// Set the ith argument of this instruction.
7710-
void setArgument(unsigned i, SILValue V) const {
7711-
getArgumentOperands()[i].set(V);
7717+
// FIXME: This is incorrect. It will be removed in the next commit.
7718+
SILArgumentConvention getArgumentConvention(unsigned index) const {
7719+
return getSubstCalleeConv().getSILArgumentConvention(index);
77127720
}
77137721

7714-
/// Return the self argument passed to this instruction.
7722+
/// Return true if 'self' is an applied argument.
77157723
bool hasSelfArgument() const {
77167724
switch (Inst->getKind()) {
77177725
case SILInstructionKind::ApplyInst:
@@ -7725,7 +7733,7 @@ class ApplySite {
77257733
}
77267734
}
77277735

7728-
/// Return the self argument passed to this instruction.
7736+
/// Return the applied 'self' argument value.
77297737
SILValue getSelfArgument() const {
77307738
switch (Inst->getKind()) {
77317739
case SILInstructionKind::ApplyInst:
@@ -7739,7 +7747,7 @@ class ApplySite {
77397747
}
77407748
}
77417749

7742-
/// Return the self operand passed to this instruction.
7750+
/// Return the 'self' apply operand.
77437751
Operand &getSelfArgumentOperand() {
77447752
switch (Inst->getKind()) {
77457753
case SILInstructionKind::ApplyInst:
@@ -7753,8 +7761,18 @@ class ApplySite {
77537761
}
77547762
}
77557763

7756-
SILArgumentConvention getArgumentConvention(unsigned index) const {
7757-
return getSubstCalleeConv().getSILArgumentConvention(index);
7764+
/// Return a list of applied arguments without self.
7765+
OperandValueArrayRef getArgumentsWithoutSelf() const {
7766+
switch (Inst->getKind()) {
7767+
case SILInstructionKind::ApplyInst:
7768+
return cast<ApplyInst>(Inst)->getArgumentsWithoutSelf();
7769+
case SILInstructionKind::BeginApplyInst:
7770+
return cast<BeginApplyInst>(Inst)->getArgumentsWithoutSelf();
7771+
case SILInstructionKind::TryApplyInst:
7772+
return cast<TryApplyInst>(Inst)->getArgumentsWithoutSelf();
7773+
default:
7774+
llvm_unreachable("not implemented for this instruction!");
7775+
}
77587776
}
77597777

77607778
static ApplySite getFromOpaqueValue(void *p) {

0 commit comments

Comments
 (0)