Skip to content

Commit d484ec7

Browse files
committed
[Compile Time Values] Implement a mandatory SIL pass to verify '@const' values
1 parent a61c400 commit d484ec7

File tree

12 files changed

+540
-189
lines changed

12 files changed

+540
-189
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ let ononeSimplificationPass = FunctionPass(name: "onone-simplification") {
4444
}
4545
}
4646

47+
let constPropagationPass = FunctionPass(name: "const-propagation") {
48+
(function: Function, context: FunctionPassContext) in
49+
runSimplification(on: function, context, preserveDebugInfo: true) {
50+
if let i = $0 as? OnoneSimplifiable {
51+
i.simplify($1)
52+
}
53+
}
54+
}
55+
4756
let simplificationPass = FunctionPass(name: "simplification") {
4857
(function: Function, context: FunctionPassContext) in
4958

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ private func registerSwiftPasses() {
8585
registerPass(releaseDevirtualizerPass, { releaseDevirtualizerPass.run($0) })
8686
registerPass(simplificationPass, { simplificationPass.run($0) })
8787
registerPass(ononeSimplificationPass, { ononeSimplificationPass.run($0) })
88+
registerPass(constPropagationPass, { constPropagationPass.run($0) })
8889
registerPass(lateOnoneSimplificationPass, { lateOnoneSimplificationPass.run($0) })
8990
registerPass(cleanupDebugStepsPass, { cleanupDebugStepsPass.run($0) })
9091
registerPass(namedReturnValueOptimization, { namedReturnValueOptimization.run($0) })

include/swift/SIL/SILConstants.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ class SymbolicValue {
260260
/// This value is represented with an inline integer representation.
261261
RK_IntegerInline,
262262

263+
/// This value is represented with a bump-pointer allocated APFloat.
264+
RK_FloatingPoint,
265+
263266
/// This value is represented with a bump-pointer allocated char array
264267
/// representing a UTF-8 encoded string.
265268
RK_String,
@@ -305,6 +308,10 @@ class SymbolicValue {
305308
/// the words of the APInt value it holds.
306309
uint64_t *integer;
307310

311+
/// When this SymbolicValue is of "FloatingPoint" kind, this
312+
/// pointer stoes information about its APFloat value
313+
APFloat *floatingPoint;
314+
308315
/// This holds the bits of an integer for an inline representation.
309316
uint64_t integerInline;
310317

@@ -390,6 +397,9 @@ class SymbolicValue {
390397
/// This is an integer constant.
391398
Integer,
392399

400+
/// This is a floating point constant.
401+
FloatingPoint,
402+
393403
/// String values may have SIL type of Builtin.RawPointer or Builtin.Word
394404
/// type.
395405
String,
@@ -480,8 +490,12 @@ class SymbolicValue {
480490
static SymbolicValue getInteger(const APInt &value,
481491
SymbolicValueAllocator &allocator);
482492

493+
static SymbolicValue getFloat(const APFloat &value,
494+
SymbolicValueAllocator &allocator);
495+
483496
APInt getIntegerValue() const;
484497
unsigned getIntegerValueBitWidth() const;
498+
APFloat getFloatValue() const;
485499

486500
/// Returns a SymbolicValue representing a UTF-8 encoded string.
487501
static SymbolicValue getString(StringRef string,

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,6 @@ LEGACY_PASS(SROABBArgs, "sroa-bb-args",
421421
"Scalar Replacement of Aggregate SIL Block Arguments")
422422
LEGACY_PASS(SimplifyBBArgs, "simplify-bb-args",
423423
"SIL Block Argument Simplification")
424-
LEGACY_PASS(SimplifyCFG, "simplify-cfg",
425-
"SIL CFG Simplification")
426424
LEGACY_PASS(SpeculativeDevirtualization, "specdevirt",
427425
"Speculative Devirtualization via Guarded Calls")
428426
LEGACY_PASS(SplitAllCriticalEdges, "split-critical-edges",

include/swift/SILOptimizer/Utils/ConstExpr.h

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,154 @@ class ConstExprEvaluator {
108108
}
109109
};
110110

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+
111259
/// A constant-expression evaluator that can be used to step through a control
112260
/// flow graph (SILFunction body) by evaluating one instruction at a time.
113261
/// This evaluator can also "skip" instructions without evaluating them and

include/swift/Sema/CSFix.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2096,7 +2096,7 @@ class NotCompileTimeLiteral final : public ContextualMismatch {
20962096
NotCompileTimeLiteral(ConstraintSystem &cs, Type paramTy, ConstraintLocator *locator);
20972097

20982098
public:
2099-
std::string getName() const override { return "replace with an literal"; }
2099+
std::string getName() const override { return "replace with a literal"; }
21002100

21012101
bool diagnose(const Solution &solution, bool asNote = false) const override;
21022102

lib/SIL/IR/SILConstants.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ void SymbolicValue::print(llvm::raw_ostream &os, unsigned indent) const {
6767
case RK_IntegerInline:
6868
os << "int: " << getIntegerValue() << "\n";
6969
return;
70+
case RK_FloatingPoint: {
71+
SmallVector<char, 0> stringFloatRepr;
72+
getFloatValue().toString(stringFloatRepr);
73+
os << "float: " << stringFloatRepr << "\n";
74+
}
7075
case RK_String:
7176
os << "string: \"" << getStringValue() << "\"\n";
7277
return;
@@ -182,6 +187,8 @@ SymbolicValue::Kind SymbolicValue::getKind() const {
182187
case RK_Integer:
183188
case RK_IntegerInline:
184189
return Integer;
190+
case RK_FloatingPoint:
191+
return FloatingPoint;
185192
case RK_String:
186193
return String;
187194
case RK_DirectAddress:
@@ -208,9 +215,11 @@ SymbolicValue::cloneInto(SymbolicValueAllocator &allocator) const {
208215
case RK_Metatype:
209216
case RK_Function:
210217
assert(0 && "cloning this representation kind is not supported");
211-
case RK_Enum:
218+
case RK_Enum:
212219
// These have trivial inline storage, just return a copy.
213220
return *this;
221+
case RK_FloatingPoint:
222+
return SymbolicValue::getFloat(getFloatValue(), allocator);
214223
case RK_IntegerInline:
215224
case RK_Integer:
216225
return SymbolicValue::getInteger(getIntegerValue(), allocator);
@@ -274,6 +283,7 @@ bool SymbolicValue::containsOnlyConstants() const {
274283
case RK_Enum:
275284
case RK_IntegerInline:
276285
case RK_Integer:
286+
case RK_FloatingPoint:
277287
case RK_String:
278288
case RK_Closure:
279289
return true;
@@ -351,6 +361,18 @@ SymbolicValue SymbolicValue::getInteger(const APInt &value,
351361
return result;
352362
}
353363

364+
SymbolicValue SymbolicValue::getFloat(const APFloat &value,
365+
SymbolicValueAllocator &allocator) {
366+
auto rawMem = allocator.allocate(APFloat::getSizeInBits(value.getSemantics()),
367+
alignof(APFloat));
368+
auto floatVal = ::new (rawMem) APFloat(value);
369+
370+
SymbolicValue result;
371+
result.representationKind = RK_FloatingPoint;
372+
result.value.floatingPoint = floatVal;
373+
return result;
374+
}
375+
354376
APInt SymbolicValue::getIntegerValue() const {
355377
assert(getKind() == Integer);
356378
if (representationKind == RK_IntegerInline) {
@@ -372,6 +394,12 @@ unsigned SymbolicValue::getIntegerValueBitWidth() const {
372394
return auxInfo.integerBitwidth;
373395
}
374396

397+
APFloat SymbolicValue::getFloatValue() const {
398+
assert(getKind() == FloatingPoint);
399+
assert(representationKind == RK_FloatingPoint);
400+
return *(value.floatingPoint);
401+
}
402+
375403
//===----------------------------------------------------------------------===//
376404
// Strings
377405
//===----------------------------------------------------------------------===//

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ target_sources(swiftSILOptimizer PRIVATE
1818
DiagnoseLifetimeIssues.cpp
1919
DiagnoseStaticExclusivity.cpp
2020
DiagnoseUnreachable.cpp
21+
DiagnoseUnknownCompileTimeValues.cpp
2122
Differentiation.cpp
2223
FlowIsolation.cpp
2324
IRGenPrepare.cpp

0 commit comments

Comments
 (0)