Skip to content

Commit c93e328

Browse files
author
Marc Rasi
committed
const evaluator part 1
1 parent 7bad558 commit c93e328

File tree

9 files changed

+1657
-0
lines changed

9 files changed

+1657
-0
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,16 @@ ERROR(shifting_all_significant_bits,none,
311311
ERROR(static_report_error, none,
312312
"static report error", ())
313313

314+
ERROR(pound_assert_condition_not_constant,none,
315+
"#assert condition not constant", ())
316+
ERROR(pound_assert_failure,none,
317+
"%0", (StringRef))
318+
319+
NOTE(constexpr_unknown_reason, none, "%0", (StringRef))
320+
NOTE(constexpr_called_from, none, "when called from here", ())
321+
NOTE(constexpr_not_evaluable, none,
322+
"expression not evaluable as constant here", ())
323+
314324
ERROR(non_physical_addressof,none,
315325
"addressof only works with purely physical lvalues; "
316326
"use `withUnsafePointer` or `withUnsafeBytes` unless you're implementing "

include/swift/SIL/SILConstants.h

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
//===--- SILConstants.h - SIL constant representation -----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This defines an interface to represent SIL level structured constants in an
14+
// memory efficient way.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_SIL_CONSTANTS_H
19+
#define SWIFT_SIL_CONSTANTS_H
20+
21+
#include "swift/SIL/SILValue.h"
22+
23+
namespace swift {
24+
class SingleValueInstruction;
25+
class SILValue;
26+
class SILBuilder;
27+
class SerializedSILLoader;
28+
29+
struct APIntSymbolicValue;
30+
struct ArraySymbolicValue;
31+
struct EnumWithPayloadSymbolicValue;
32+
struct UnknownSymbolicValue;
33+
34+
/// When we fail to constant fold a value, this captures a reason why,
35+
/// allowing the caller to produce a specific diagnostic. The "Unknown"
36+
/// SymbolicValue representation also includes a pointer to the SILNode in
37+
/// question that was problematic.
38+
enum class UnknownReason {
39+
// TODO: Eliminate the default code, by making classifications for each
40+
// failure mode.
41+
Default,
42+
43+
/// The constant expression was too big. This is reported on a random
44+
/// instruction within the constexpr that triggered the issue.
45+
TooManyInstructions,
46+
47+
/// A control flow loop was found.
48+
Loop,
49+
50+
/// Integer overflow detected.
51+
Overflow,
52+
53+
/// Unspecified trap detected.
54+
Trap,
55+
};
56+
57+
/// This is the symbolic value tracked for each SILValue in a scope. We
58+
/// support multiple representational forms for the constant node in order to
59+
/// avoid pointless memory bloat + copying. This is intended to be a
60+
/// light-weight POD type we can put in hash tables and pass around by-value.
61+
///
62+
/// Internally, this value has multiple ways to represent the same sorts of
63+
/// symbolic values (e.g. to save memory). It provides a simpler public
64+
/// interface though.
65+
class SymbolicValue {
66+
private:
67+
enum RepresentationKind {
68+
/// This symbolic value cannot be determined, carries multiple values
69+
/// (i.e., varies dynamically at the top level), or is of some type that
70+
/// we cannot analyze and propagate (e.g. NSObject).
71+
///
72+
RK_Unknown,
73+
74+
/// This value is known to be a metatype reference. The type is stored
75+
/// in the "metatype" member.
76+
RK_Metatype,
77+
78+
/// This value is known to be a function reference, e.g. through
79+
/// function_ref directly, or a devirtualized method reference.
80+
RK_Function,
81+
82+
/// This value is represented with a bump-pointer allocated APInt.
83+
RK_Integer,
84+
85+
/// This value is represented with an inline integer representation.
86+
RK_IntegerInline,
87+
88+
/// This value is an array, struct, or tuple of constants. This is
89+
/// tracked by the "aggregate" member of the value union.
90+
RK_Aggregate,
91+
};
92+
93+
union {
94+
/// When the value is Unknown, this contains information about the
95+
/// unfoldable part of the computation.
96+
UnknownSymbolicValue *unknown;
97+
98+
/// This is always a SILType with an object category. This is the value
99+
/// of the underlying instance type, not the MetatypeType.
100+
TypeBase *metatype;
101+
102+
SILFunction *function;
103+
104+
/// When this SymbolicValue is of "Integer" kind, this pointer stores
105+
/// the words of the APInt value it holds.
106+
uint64_t *integer;
107+
108+
/// This holds the bits of an integer for an inline representation.
109+
uint64_t integerInline;
110+
111+
/// When this SymbolicValue is of "Aggregate" kind, this pointer stores
112+
/// information about the array elements and count.
113+
const SymbolicValue *aggregate;
114+
} value;
115+
116+
RepresentationKind representationKind : 8;
117+
118+
union {
119+
/// This is the reason code for RK_Unknown values.
120+
UnknownReason unknown_reason : 32;
121+
122+
/// This is the number of bits in an RK_Integer or RK_IntegerInline
123+
/// representation, which makes the number of entries in the list derivable.
124+
unsigned integer_bitwidth;
125+
126+
/// This is the number of elements for an RK_Aggregate representation.
127+
unsigned aggregate_numElements;
128+
} aux;
129+
130+
public:
131+
/// This enum is used to indicate the sort of value held by a SymbolicValue
132+
/// independent of its concrete representation. This is the public
133+
/// interface to SymbolicValue.
134+
enum Kind {
135+
/// This is a value that isn't a constant.
136+
Unknown,
137+
138+
/// This is a known metatype value.
139+
Metatype,
140+
141+
/// This is a function, represented as a SILFunction.
142+
Function,
143+
144+
/// This is an integer constant.
145+
Integer,
146+
147+
/// This can be an array, struct, tuple, etc.
148+
Aggregate,
149+
};
150+
151+
/// For constant values, return the type classification of this value.
152+
Kind getKind() const;
153+
154+
/// Return true if this represents a constant value.
155+
bool isConstant() const {
156+
auto kind = getKind();
157+
return kind != Unknown;
158+
}
159+
160+
static SymbolicValue getUnknown(SILNode *node, UnknownReason reason,
161+
llvm::ArrayRef<SourceLoc> callStack,
162+
llvm::BumpPtrAllocator &allocator);
163+
164+
/// Return true if this represents an unknown result.
165+
bool isUnknown() const { return getKind() == Unknown; }
166+
167+
/// Return the call stack for an unknown result.
168+
ArrayRef<SourceLoc> getUnknownCallStack() const;
169+
170+
/// Return the node that triggered an unknown result.
171+
SILNode *getUnknownNode() const;
172+
173+
/// Return the reason an unknown result was generated.
174+
UnknownReason getUnknownReason() const;
175+
176+
static SymbolicValue getMetatype(CanType type) {
177+
SymbolicValue result;
178+
result.representationKind = RK_Metatype;
179+
result.value.metatype = type.getPointer();
180+
return result;
181+
}
182+
183+
CanType getMetatypeValue() const {
184+
assert(representationKind == RK_Metatype);
185+
return CanType(value.metatype);
186+
}
187+
188+
static SymbolicValue getFunction(SILFunction *fn) {
189+
assert(fn && "Function cannot be null");
190+
SymbolicValue result;
191+
result.representationKind = RK_Function;
192+
result.value.function = fn;
193+
return result;
194+
}
195+
196+
SILFunction *getFunctionValue() const {
197+
assert(getKind() == Function);
198+
return value.function;
199+
}
200+
201+
static SymbolicValue getInteger(int64_t value, unsigned bitWidth);
202+
static SymbolicValue getInteger(const APInt &value,
203+
llvm::BumpPtrAllocator &allocator);
204+
205+
APInt getIntegerValue() const;
206+
unsigned getIntegerValueBitWidth() const;
207+
208+
/// This returns an aggregate value with the specified elements in it. This
209+
/// copies the elements into the specified allocator.
210+
static SymbolicValue getAggregate(ArrayRef<SymbolicValue> elements,
211+
llvm::BumpPtrAllocator &allocator);
212+
213+
ArrayRef<SymbolicValue> getAggregateValue() const;
214+
215+
//===--------------------------------------------------------------------===//
216+
// Helpers
217+
218+
/// Dig through single element aggregates, return the ultimate thing inside of
219+
/// it. This is useful when dealing with integers and floats, because they
220+
/// are often wrapped in single-element struct wrappers.
221+
SymbolicValue lookThroughSingleElementAggregates() const;
222+
223+
/// Given that this is an 'Unknown' value, emit diagnostic notes providing
224+
/// context about what the problem is. If there is no location for some
225+
/// reason, we fall back to using the specified location.
226+
void emitUnknownDiagnosticNotes(SILLocation fallbackLoc);
227+
228+
/// Clone this SymbolicValue into the specified allocator and return the new
229+
/// version. This only works for valid constants.
230+
SymbolicValue cloneInto(llvm::BumpPtrAllocator &allocator) const;
231+
232+
void print(llvm::raw_ostream &os, unsigned indent = 0) const;
233+
void dump() const;
234+
};
235+
236+
static_assert(sizeof(SymbolicValue) == 2 * sizeof(void *),
237+
"SymbolicValue should stay small and POD");
238+
239+
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, SymbolicValue val) {
240+
val.print(os);
241+
return os;
242+
}
243+
244+
} // end namespace swift
245+
246+
#endif
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===--- ConstExpr.h - Constant expression evaluator -----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This defines an interface to evaluate Swift language level constant
14+
// expressions. Its model is intended to be general and reasonably powerful,
15+
// with the goal of standardization in a future version of Swift.
16+
//
17+
// Constant expressions are functions without side effects that take constant
18+
// values and return constant values. These constants may be integer, floating
19+
// point, and string values, or arrays thereof (up to 1024 elements). We allow
20+
// abstractions to be built out of fragile structs and tuples.
21+
//
22+
//===----------------------------------------------------------------------===//
23+
24+
#ifndef SWIFT_SILOPTIMIZER_CONSTEXPR_H
25+
#define SWIFT_SILOPTIMIZER_CONSTEXPR_H
26+
27+
#include "swift/Basic/LLVM.h"
28+
#include "swift/Basic/SourceLoc.h"
29+
#include "llvm/Support/Allocator.h"
30+
31+
namespace swift {
32+
class ApplyInst;
33+
class Operand;
34+
class SILInstruction;
35+
class SILModule;
36+
class SILNode;
37+
class SILValue;
38+
class SymbolicValue;
39+
enum class UnknownReason;
40+
41+
/// This class is the main entrypoint for evaluating constant expressions. It
42+
/// also handles caching of previously computed constexpr results.
43+
class ConstExprEvaluator {
44+
/// This is a long-lived bump pointer allocator that holds the arguments and
45+
/// result values for the cached constexpr calls we have already analyzed.
46+
llvm::BumpPtrAllocator &allocator;
47+
48+
/// The current call stack, used for providing accurate diagnostics.
49+
llvm::SmallVector<SourceLoc, 4> callStack;
50+
51+
ConstExprEvaluator(const ConstExprEvaluator &) = delete;
52+
void operator=(const ConstExprEvaluator &) = delete;
53+
54+
public:
55+
explicit ConstExprEvaluator(SILModule &m);
56+
~ConstExprEvaluator();
57+
58+
llvm::BumpPtrAllocator &getAllocator() { return allocator; }
59+
60+
void pushCallStack(SourceLoc loc) { callStack.push_back(loc); }
61+
62+
void popCallStack() {
63+
assert(!callStack.empty());
64+
callStack.pop_back();
65+
}
66+
67+
const llvm::SmallVector<SourceLoc, 4> &getCallStack() { return callStack; }
68+
69+
// As SymbolicValue::getUnknown(), but handles passing the call stack and
70+
// allocator.
71+
SymbolicValue getUnknown(SILNode *node, UnknownReason reason);
72+
73+
/// Analyze the specified values to determine if they are constant values.
74+
/// This is done in code that is not necessarily itself a constexpr
75+
/// function. The results are added to the results list which is a parallel
76+
/// structure to the input values.
77+
///
78+
/// TODO: Return information about which callees were found to be
79+
/// constexprs, which would allow the caller to delete dead calls to them
80+
/// that occur after after folding them.
81+
void computeConstantValues(ArrayRef<SILValue> values,
82+
SmallVectorImpl<SymbolicValue> &results);
83+
};
84+
85+
} // end namespace swift
86+
#endif

lib/SIL/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ add_swift_library(swiftSIL STATIC
1818
SILArgument.cpp
1919
SILBasicBlock.cpp
2020
SILBuilder.cpp
21+
SILConstants.cpp
2122
SILCoverageMap.cpp
2223
SILDebugScope.cpp
2324
SILDeclRef.cpp

0 commit comments

Comments
 (0)