Skip to content

Commit 267d63e

Browse files
committed
[sil] Instead of returning an ArrayRef<SILValue> for SILInstruction::getResults(), use SILInstructionResultArray.
The reason that I am doing this is in preparation for adding support for MultipleValueInstruction. This enables us to avoid type issues and also ensures that we do not increase the size of SingleValueInstruction while we are doing it. The MultipleValueInstruction commit will come soon. rdar://31521023
1 parent 0ccf4fa commit 267d63e

File tree

4 files changed

+236
-26
lines changed

4 files changed

+236
-26
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 127 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class DeclRefExpr;
4444
class FloatLiteralExpr;
4545
class FuncDecl;
4646
class IntegerLiteralExpr;
47+
class NonValueInstruction;
4748
class SILBasicBlock;
4849
class SILBuilder;
4950
class SILDebugLocation;
@@ -87,6 +88,114 @@ SILInstructionKind getSILInstructionKind(StringRef InstName);
8788
/// Map SILInstructionKind to a corresponding SILInstruction name.
8889
StringRef getSILInstructionName(SILInstructionKind Kind);
8990

91+
/// A formal SIL reference to a list of values, suitable for use as the result
92+
/// of a SILInstruction.
93+
///
94+
/// *NOTE* Most multiple value instructions will not have many results, so if we
95+
/// want we can cache up to 3 bytes in the lower bits of the value.
96+
///
97+
/// *NOTE* Most of this defined out of line further down in the file to work
98+
/// around forward declaration issues.
99+
class SILInstructionResultArray {
100+
const uint8_t *Pointer;
101+
unsigned Size;
102+
103+
public:
104+
SILInstructionResultArray() : Pointer(nullptr), Size(0) {}
105+
SILInstructionResultArray(const SingleValueInstruction *SVI);
106+
107+
SILInstructionResultArray(const SILInstructionResultArray &Other) = default;
108+
SILInstructionResultArray &
109+
operator=(const SILInstructionResultArray &Other) = default;
110+
SILInstructionResultArray(SILInstructionResultArray &&Other) = default;
111+
SILInstructionResultArray &
112+
operator=(SILInstructionResultArray &&Other) = default;
113+
114+
SILValue operator[](size_t Index) const;
115+
116+
bool empty() const { return Size == 0; }
117+
118+
size_t size() const { return Size; }
119+
120+
class iterator;
121+
122+
iterator begin() const;
123+
iterator end() const;
124+
125+
using range = llvm::iterator_range<iterator>;
126+
range getValues() const;
127+
128+
using type_range = llvm::iterator_range<
129+
llvm::mapped_iterator<iterator, std::function<SILType(SILValue)>>>;
130+
type_range getTypes() const;
131+
132+
bool operator==(const SILInstructionResultArray &rhs);
133+
bool operator!=(const SILInstructionResultArray &other) {
134+
return !(*this == other);
135+
}
136+
137+
/// Returns true if both this and \p rhs have the same result types.
138+
///
139+
/// *NOTE* This does not imply that the actual return SILValues are the
140+
/// same. Just that the types are the same.
141+
bool hasSameTypes(const SILInstructionResultArray &rhs);
142+
143+
private:
144+
/// Return the offset 1 past the end of the array or None if we are not
145+
/// actually storing anything.
146+
Optional<unsigned> getStartOffset() const {
147+
return empty() ? None : Optional<unsigned>(0);
148+
}
149+
150+
/// Return the offset 1 past the end of the array or None if we are not
151+
/// actually storing anything.
152+
Optional<unsigned> getEndOffset() const {
153+
return empty() ? None : Optional<unsigned>(size());
154+
}
155+
};
156+
157+
class SILInstructionResultArray::iterator
158+
: public std::iterator<std::bidirectional_iterator_tag, SILValue,
159+
ptrdiff_t> {
160+
/// Our "parent" array.
161+
///
162+
/// This is actually a value type reference into a SILInstruction of some
163+
/// sort. So we can just have our own copy. This also allows us to not worry
164+
/// about our underlying array having too short of a lifetime.
165+
SILInstructionResultArray Parent;
166+
167+
/// The index into the parent array.
168+
Optional<unsigned> Index;
169+
170+
public:
171+
iterator() = default;
172+
iterator(const SILInstructionResultArray &Parent,
173+
Optional<unsigned> Index = 0)
174+
: Parent(Parent), Index(Index) {}
175+
176+
SILValue operator*() const { return Parent[Index.getValue()]; }
177+
178+
SILValue operator->() const { return operator*(); }
179+
180+
iterator &operator++() {
181+
++Index.getValue();
182+
return *this;
183+
}
184+
185+
iterator operator++(int) {
186+
iterator copy = *this;
187+
++Index.getValue();
188+
return copy;
189+
}
190+
191+
friend bool operator==(iterator lhs, iterator rhs) {
192+
assert(lhs.Parent.Pointer == rhs.Parent.Pointer);
193+
return lhs.Index == rhs.Index;
194+
}
195+
196+
friend bool operator!=(iterator lhs, iterator rhs) { return !(lhs == rhs); }
197+
};
198+
90199
/// This is the root class for all instructions that can be used as the
91200
/// contents of a Swift SILBasicBlock.
92201
///
@@ -143,7 +252,7 @@ class SILInstruction
143252

144253
/// An internal method which retrieves the result values of the
145254
/// instruction as an array of ValueBase objects.
146-
ArrayRef<ValueBase> getResultsImpl() const;
255+
SILInstructionResultArray getResultsImpl() const;
147256

148257
protected:
149258
SILInstruction(SILInstructionKind kind, SILDebugLocation DebugLoc)
@@ -318,18 +427,13 @@ class SILInstruction
318427
getAllOperands()[Num1].swap(getAllOperands()[Num2]);
319428
}
320429

321-
using ResultArrayRef =
322-
ArrayRefView<ValueBase,SILValue,projectValueBaseAsSILValue>;
323-
324430
/// Return the list of results produced by this instruction.
325-
ResultArrayRef getResults() const { return getResultsImpl(); }
326-
327-
using ResultTypeArrayRef =
328-
ArrayRefView<ValueBase,SILType,projectValueBaseType>;
431+
SILInstructionResultArray getResults() const { return getResultsImpl(); }
432+
unsigned getNumResults() const { return getResults().size(); }
329433

330434
/// Return the types of the results produced by this instruction.
331-
ResultTypeArrayRef getResultTypes() const {
332-
return getResultsImpl();
435+
SILInstructionResultArray::type_range getResultTypes() const {
436+
return getResultsImpl().getTypes();
333437
}
334438

335439
MemoryBehavior getMemoryBehavior() const;
@@ -364,9 +468,9 @@ class SILInstruction
364468
return false;
365469
}
366470

367-
if (getResultTypes() != RHS->getResultTypes())
471+
if (!getResults().hasSameTypes(RHS->getResults()))
368472
return false;
369-
473+
370474
// Check operands.
371475
for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
372476
if (!opEqual(getOperand(i), RHS->getOperand(i)))
@@ -504,8 +608,8 @@ class SingleValueInstruction : public SILInstruction, public ValueBase {
504608
}
505609

506610
friend class SILInstruction;
507-
ArrayRef<ValueBase> getResultsImpl() const {
508-
return ArrayRef<ValueBase>(this, 1);
611+
SILInstructionResultArray getResultsImpl() const {
612+
return SILInstructionResultArray(this);
509613
}
510614
public:
511615
SingleValueInstruction(SILInstructionKind kind, SILDebugLocation loc,
@@ -547,9 +651,7 @@ class SingleValueInstruction : public SILInstruction, public ValueBase {
547651
}
548652

549653
/// Override this to reflect the more efficient access pattern.
550-
ResultArrayRef getResults() const {
551-
return getResultsImpl();
552-
}
654+
SILInstructionResultArray getResults() const { return getResultsImpl(); }
553655

554656
static bool classof(const SILNode *node) {
555657
return isSingleValueInstKind(node->getKind());
@@ -599,7 +701,14 @@ class NonValueInstruction : public SILInstruction {
599701

600702
/// Doesn't produce any results.
601703
SILType getType() const = delete;
602-
ResultArrayRef getResults() const = delete;
704+
SILInstructionResultArray getResults() const = delete;
705+
706+
static bool classof(const ValueBase *value) = delete;
707+
static bool classof(const SILNode *N) {
708+
return N->getKind() >= SILNodeKind::First_NonValueInstruction &&
709+
N->getKind() <= SILNodeKind::Last_NonValueInstruction;
710+
}
711+
static bool classof(const NonValueInstruction *) { return true; }
603712
};
604713
#define DEFINE_ABSTRACT_NON_VALUE_INST_BOILERPLATE(ID) \
605714
static bool classof(const ValueBase *value) = delete; \

include/swift/SIL/SILNodes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,8 @@ NON_VALUE_INST(AllocGlobalInst, alloc_global,
613613
NON_VALUE_INST(CondFailInst, cond_fail,
614614
SILInstruction, MayHaveSideEffects, DoesNotRelease)
615615

616+
NODE_RANGE(NonValueInstruction, UnreachableInst, CondFailInst)
617+
616618
NODE_RANGE(SILInstruction, AllocStackInst, CondFailInst)
617619
NODE_RANGE(SILNode, SILPHIArgument, CondFailInst)
618620

lib/SIL/SILInstruction.cpp

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,19 +172,20 @@ void SILInstruction::dropAllReferences() {
172172
}
173173
}
174174

175-
ArrayRef<ValueBase> SILInstruction::getResultsImpl() const {
175+
SILInstructionResultArray SILInstruction::getResultsImpl() const {
176176
switch (getKind()) {
177177
#define NON_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \
178178
case SILInstructionKind::ID:
179179
#include "swift/SIL/SILNodes.def"
180-
return {};
180+
return SILInstructionResultArray();
181181

182182
#define SINGLE_VALUE_INST(ID, NAME, PARENT, MEMBEHAVIOR, MAYRELEASE) \
183183
case SILInstructionKind::ID:
184184
#include "swift/SIL/SILNodes.def"
185-
return static_cast<const SingleValueInstruction *>(this)->getResultsImpl();
185+
return SILInstructionResultArray(
186+
static_cast<const SingleValueInstruction *>(this));
186187

187-
// add any multi-result instructions here...
188+
// add any multi-result instructions here...
188189
}
189190
llvm_unreachable("bad kind");
190191
}
@@ -1174,3 +1175,101 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS,
11741175

11751176
llvm_unreachable("Unhandled ReleasingBehavior in switch.");
11761177
}
1178+
1179+
//===----------------------------------------------------------------------===//
1180+
// SILInstructionResultArray
1181+
//===----------------------------------------------------------------------===//
1182+
1183+
SILInstructionResultArray::SILInstructionResultArray(
1184+
const SingleValueInstruction *SVI)
1185+
: Pointer(), Size(1) {
1186+
// Make sure that even though we are munging things, we are able to get back
1187+
// the original value, types, and operands.
1188+
SILValue originalValue(SVI);
1189+
SILType originalType = SVI->getType();
1190+
(void)originalValue;
1191+
(void)originalType;
1192+
1193+
// *PLEASE READ BEFORE CHANGING*
1194+
//
1195+
// Since SingleValueInstruction is both a ValueBase and a SILInstruction, but
1196+
// SILInstruction is the first parent, we need to ensure that our ValueBase *
1197+
// pointer is properly offset. by first static casting to ValueBase and then
1198+
// going back to a uint8_t *.
1199+
auto *Value = static_cast<const ValueBase *>(SVI);
1200+
assert(uintptr_t(Value) != uintptr_t(SVI) &&
1201+
"Expected value to be offset from SVI since it is not the first "
1202+
"multi-inheritence parent");
1203+
Pointer = reinterpret_cast<const uint8_t *>(Value);
1204+
1205+
assert(originalValue == (*this)[0] &&
1206+
"Wrong value returned for single result");
1207+
assert(originalType == (*this)[0]->getType());
1208+
1209+
auto ValueRange = getValues();
1210+
(void)ValueRange;
1211+
assert(1 == std::distance(ValueRange.begin(), ValueRange.end()));
1212+
assert(originalValue == *ValueRange.begin());
1213+
1214+
auto TypedRange = getTypes();
1215+
(void)TypedRange;
1216+
assert(1 == std::distance(TypedRange.begin(), TypedRange.end()));
1217+
assert(originalType == *TypedRange.begin());
1218+
1219+
SILInstructionResultArray Copy = *this;
1220+
(void)Copy;
1221+
assert(Copy.hasSameTypes(*this));
1222+
assert(Copy == *this);
1223+
}
1224+
1225+
SILValue SILInstructionResultArray::operator[](size_t Index) const {
1226+
assert(Index < Size && "Index out of bounds");
1227+
// Today we only have single instruction results so offset will always be
1228+
// zero. Once we have multiple instruction results, this will be equal to
1229+
// sizeof(MultipleValueInstructionResult)*Index. This is safe even to do with
1230+
// SingleValueInstruction since index will always be zero for the offset.
1231+
size_t Offset = 0;
1232+
return SILValue(reinterpret_cast<const ValueBase *>(&Pointer[Offset]));
1233+
}
1234+
1235+
bool SILInstructionResultArray::hasSameTypes(
1236+
const SILInstructionResultArray &rhs) {
1237+
auto &lhs = *this;
1238+
if (lhs.size() != rhs.size())
1239+
return false;
1240+
for (unsigned i : indices(lhs)) {
1241+
if (lhs[i]->getType() != rhs[i]->getType())
1242+
return false;
1243+
}
1244+
return true;
1245+
}
1246+
1247+
bool SILInstructionResultArray::
1248+
operator==(const SILInstructionResultArray &other) {
1249+
if (size() != other.size())
1250+
return false;
1251+
for (auto i : indices(*this))
1252+
if ((*this)[i] != other[i])
1253+
return false;
1254+
return true;
1255+
}
1256+
1257+
SILInstructionResultArray::type_range
1258+
SILInstructionResultArray::getTypes() const {
1259+
std::function<SILType(SILValue)> F = [](SILValue V) -> SILType {
1260+
return V->getType();
1261+
};
1262+
return {llvm::map_iterator(begin(), F), llvm::map_iterator(end(), F)};
1263+
}
1264+
1265+
SILInstructionResultArray::iterator SILInstructionResultArray::begin() const {
1266+
return iterator(*this, getStartOffset());
1267+
}
1268+
1269+
SILInstructionResultArray::iterator SILInstructionResultArray::end() const {
1270+
return iterator(*this, getEndOffset());
1271+
}
1272+
1273+
SILInstructionResultArray::range SILInstructionResultArray::getValues() const {
1274+
return {begin(), end()};
1275+
}

lib/SIL/SILPrinter.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -660,13 +660,13 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
660660
/// Print out the users of the SILValue \p V. Return true if we printed out
661661
/// either an id or a use list. Return false otherwise.
662662
bool printUsersOfSILNode(const SILNode *node, bool printedSlashes) {
663-
SILInstruction::ResultArrayRef values;
663+
TinyPtrVector<SILValue> values;
664664
if (auto value = dyn_cast<ValueBase>(node)) {
665665
// The base pointer of the ultimate ArrayRef here is just the
666666
// ValueBase; we aren't forming a reference to a temporary array.
667-
values = ArrayRef<ValueBase>(value, 1);
668-
} else if (auto inst = dyn_cast<SILInstruction>(node)) {
669-
values = inst->getResults();
667+
values.push_back(value);
668+
} else if (auto *inst = dyn_cast<SILInstruction>(node)) {
669+
copy(inst->getResults(), std::back_inserter(values));
670670
}
671671

672672
// If the set of values is empty, we need to print the ID of

0 commit comments

Comments
 (0)