@@ -108,6 +108,154 @@ class ConstExprEvaluator {
108
108
}
109
109
};
110
110
111
+ enum class WellKnownFunction {
112
+ // Array.init()
113
+ ArrayInitEmpty,
114
+ // Array._allocateUninitializedArray
115
+ AllocateUninitializedArray,
116
+ // Array._endMutation
117
+ EndArrayMutation,
118
+ // _finalizeUninitializedArray
119
+ FinalizeUninitializedArray,
120
+ // Array.append(_:)
121
+ ArrayAppendElement,
122
+ // String.init()
123
+ StringInitEmpty,
124
+ // String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
125
+ StringMakeUTF8,
126
+ // static String.append (_: String, _: inout String)
127
+ StringAppend,
128
+ // static String.== infix(_: String)
129
+ StringEquals,
130
+ // String.percentEscapedString.getter
131
+ StringEscapePercent,
132
+ // BinaryInteger.description.getter
133
+ BinaryIntegerDescription,
134
+ // _assertionFailure(_: StaticString, _: StaticString, file: StaticString,...)
135
+ AssertionFailure,
136
+ // A function taking one argument that prints the symbolic value of the
137
+ // argument during constant evaluation. This must only be used for debugging.
138
+ DebugPrint
139
+ };
140
+
141
+ // ===----------------------------------------------------------------------===//
142
+ // ConstExprFunctionState implementation.
143
+ // ===----------------------------------------------------------------------===//
144
+
145
+ // / This type represents the state of computed values within a function
146
+ // / as evaluation happens. A separate instance of this is made for each
147
+ // / callee in a call chain to represent the constant values given the set of
148
+ // / formal parameters that callee was invoked with.
149
+ class ConstExprFunctionState {
150
+ // / This is the evaluator that is computing this function state. We use it to
151
+ // / allocate space for values and to query the call stack.
152
+ ConstExprEvaluator &evaluator;
153
+
154
+ // / If we are analyzing the body of a constexpr function, this is the
155
+ // / function. This is null for the top-level expression.
156
+ SILFunction *fn;
157
+
158
+ // / If we have a function being analyzed, this is the substitutionMap for
159
+ // / the call to it.
160
+ // / substitutionMap specifies a mapping from all of the protocol and type
161
+ // / requirements in the generic signature down to concrete conformances and
162
+ // / concrete types.
163
+ SubstitutionMap substitutionMap;
164
+
165
+ // / This keeps track of the number of instructions we've evaluated. If this
166
+ // / goes beyond the execution cap, then we start returning unknown values.
167
+ unsigned &numInstEvaluated;
168
+
169
+ // / This is a state of previously analyzed values, maintained and filled in
170
+ // / by getConstantValue. This does not hold the memory referred to by SIL
171
+ // / addresses.
172
+ llvm::DenseMap<SILValue, SymbolicValue> calculatedValues;
173
+
174
+ // / If a SILValue is not bound to a SymbolicValue in the calculatedValues,
175
+ // / try to compute it recursively by visiting its defining instruction.
176
+ bool recursivelyComputeValueIfNotInState = false ;
177
+
178
+ public:
179
+ ConstExprFunctionState (ConstExprEvaluator &evaluator, SILFunction *fn,
180
+ SubstitutionMap substitutionMap,
181
+ unsigned &numInstEvaluated,
182
+ bool enableTopLevelEvaluation);
183
+
184
+ // / Pretty print the state to stderr.
185
+ void dump () const ;
186
+
187
+ void setValue (SILValue value, SymbolicValue symVal);
188
+
189
+ // / Return the symbolic value for a SILValue if it is bound in the interpreter
190
+ // / state. If not, return None.
191
+ std::optional<SymbolicValue> lookupValue (SILValue value);
192
+
193
+ // / Invariant: Before the call, `calculatedValues` must not contain `addr`
194
+ // / as a key.
195
+ SymbolicValue createMemoryObject (SILValue addr, SymbolicValue initialValue);
196
+
197
+ // / Return the SymbolicValue for the specified SIL value. If the SIL value is
198
+ // / not in \c calculatedValues, try computing the SymbolicValue recursively
199
+ // / if \c recursivelyComputeValueIfNotInState flag is set.
200
+ SymbolicValue getConstantValue (SILValue value);
201
+
202
+ // / Evaluate the specified instruction in a flow sensitive way, for use by
203
+ // / the constexpr function evaluator. This does not handle control flow
204
+ // / statements.
205
+ std::optional<SymbolicValue> evaluateFlowSensitive (SILInstruction *inst);
206
+
207
+ // / Evaluate a branch or non-branch instruction and if the evaluation was
208
+ // / successful, return the next instruction from where the evaluation must
209
+ // / continue.
210
+ // / \param instI basic-block iterator pointing to the instruction to evaluate.
211
+ // / \param visitedBlocks basic blocks already visited during evaluation.
212
+ // / This is used to detect loops.
213
+ // / \returns a pair where the first and second elements are defined as
214
+ // / follows:
215
+ // / If the evaluation of the instruction is successful, the first element
216
+ // / is the iterator to the next instruction from the where the evaluation
217
+ // / must continue. Otherwise, it is None.
218
+ // /
219
+ // / Second element is None, if the evaluation is successful.
220
+ // / Otherwise, is an unknown symbolic value that contains the error.
221
+ std::pair<std::optional<SILBasicBlock::iterator>,
222
+ std::optional<SymbolicValue>>
223
+ evaluateInstructionAndGetNext (
224
+ SILBasicBlock::iterator instI,
225
+ SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks);
226
+
227
+ Type substituteGenericParamsAndSimplify (Type ty);
228
+ CanType substituteGenericParamsAndSimplify (CanType ty) {
229
+ return substituteGenericParamsAndSimplify (Type (ty))->getCanonicalType ();
230
+ }
231
+ SymbolicValue computeConstantValue (SILValue value);
232
+ SymbolicValue computeConstantValueBuiltin (BuiltinInst *inst);
233
+
234
+ std::optional<SymbolicValue> computeCallResult (ApplyInst *apply);
235
+
236
+ std::optional<SymbolicValue> computeOpaqueCallResult (ApplyInst *apply,
237
+ SILFunction *callee);
238
+
239
+ std::optional<SymbolicValue>
240
+ computeWellKnownCallResult (ApplyInst *apply, WellKnownFunction callee);
241
+
242
+ // / Evaluate a closure creation instruction which is either a partial_apply
243
+ // / instruction or a thin_to_think_function instruction. On success, this
244
+ // / function will bind the \c closureInst parameter to its symbolic value.
245
+ // / On failure, it returns the unknown symbolic value that captures the error.
246
+ std::optional<SymbolicValue>
247
+ evaluateClosureCreation (SingleValueInstruction *closureInst);
248
+
249
+ SymbolicValue getSingleWriterAddressValue (SILValue addr);
250
+ SymbolicValue getConstAddrAndLoadResult (SILValue addr);
251
+ SymbolicValue loadAddrValue (SILValue addr, SymbolicValue addrVal);
252
+ std::optional<SymbolicValue> computeFSStore (SymbolicValue storedCst,
253
+ SILValue dest);
254
+
255
+ private:
256
+ std::optional<SymbolicValue> initializeAddressFromSingleWriter (SILValue addr);
257
+ };
258
+
111
259
// / A constant-expression evaluator that can be used to step through a control
112
260
// / flow graph (SILFunction body) by evaluating one instruction at a time.
113
261
// / This evaluator can also "skip" instructions without evaluating them and
0 commit comments