Skip to content

Commit f91f967

Browse files
author
Marc Rasi
committed
constant interpreter: basic integer operations
Implements a constant interpreter that can deal with basic integer operations. Summary of the features that it includes: * builtin integer values, and builtin integer insts * struct and tuple values, and insts that construct and extract them (necessary to use stdlib integers) * function referencing and application (necessary to call stdlib integer functions) * error handling data structures and logic, for telling you why your value is not evaluatable * metatype values (not necessary for integers, but it's only a few extra lines, so I thought it would be more trouble than it's worth to put them in a separate PR) * conditional branches (ditto)
1 parent aa3e16d commit f91f967

File tree

9 files changed

+1685
-0
lines changed

9 files changed

+1685
-0
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,16 @@ ERROR(shifting_all_significant_bits,none,
298298
ERROR(static_report_error, none,
299299
"static report error", ())
300300

301+
ERROR(pound_assert_condition_not_constant,none,
302+
"#assert condition not constant", ())
303+
ERROR(pound_assert_failure,none,
304+
"%0", (StringRef))
305+
306+
NOTE(constexpr_unknown_reason, none, "%0", (StringRef))
307+
NOTE(constexpr_called_from, none, "when called from here", ())
308+
NOTE(constexpr_not_evaluable, none,
309+
"expression not evaluable as constant here", ())
310+
301311
ERROR(non_physical_addressof,none,
302312
"addressof only works with purely physical lvalues; "
303313
"use `withUnsafePointer` or `withUnsafeBytes` unless you're implementing "

include/swift/SIL/SILConstants.h

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

lib/SIL/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_swift_host_library(swiftSIL STATIC
2020
SILArgument.cpp
2121
SILBasicBlock.cpp
2222
SILBuilder.cpp
23+
SILConstants.cpp
2324
SILCoverageMap.cpp
2425
SILDebugScope.cpp
2526
SILDeclRef.cpp

0 commit comments

Comments
 (0)