Skip to content

Commit f133b76

Browse files
committed
---
yaml --- r: 319199 b: refs/heads/master-rebranch c: e371ca2 h: refs/heads/master i: 319197: 8a70f4f 319195: fc4eb3a 319191: f5175b8 319183: 312805b 319167: a8d391d
1 parent bfcab7a commit f133b76

File tree

8 files changed

+583
-30
lines changed

8 files changed

+583
-30
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1457,4 +1457,4 @@ refs/tags/swift-DEVELOPMENT-SNAPSHOT-2019-08-02-a: ddd2b2976aa9bfde5f20fe37f6bd2
14571457
refs/tags/swift-DEVELOPMENT-SNAPSHOT-2019-08-03-a: 171cc166f2abeb5ca2a4003700a8a78a108bd300
14581458
refs/heads/benlangmuir-patch-1: baaebaf39d52f3bf36710d4fe40cf212e996b212
14591459
refs/heads/i-do-redeclare: 8c4e6d5de5c1e3f0a2cedccf319df713ea22c48e
1460-
refs/heads/master-rebranch: 35623242dc9289b3d7b088af5df0a3f31bc83d88
1460+
refs/heads/master-rebranch: e371ca2296ca82dc089693dd8ff5eb2f3ad3a79d

branches/master-rebranch/include/swift/AST/DiagnosticsSIL.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,13 @@ NOTE(constexpr_witness_call_with_no_target_found, none,
395395
NOTE(constexpr_witness_call_found_here, none,
396396
"witness method call found here", ())
397397

398+
NOTE(constexpr_unknown_control_flow_due_to_skip,none, "branch depends on "
399+
"non-constant value produced by an unevaluated instructions", ())
400+
NOTE(constexpr_returned_by_unevaluated_instruction,none,
401+
"return value of an unevaluated instruction is not a constant", ())
402+
NOTE(constexpr_mutated_by_unevaluated_instruction,none, "value mutable by an "
403+
"unevaluated instruction is not a constant", ())
404+
398405
ERROR(non_physical_addressof,none,
399406
"addressof only works with purely physical lvalues; "
400407
"use `withUnsafePointer` or `withUnsafeBytes` unless you're implementing "

branches/master-rebranch/include/swift/SIL/SILConstants.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ enum class UnknownReason {
9090
/// A top-level value has multiple writers. This is only relevant in the
9191
/// non-flow-sensitive evaluation mode, which is used by #assert.
9292
MutipleTopLevelWriters,
93+
94+
/// Indicates the return value of an instruction that was not evaluated during
95+
/// interpretation.
96+
ReturnedByUnevaluatedInstruction,
97+
98+
/// Indicates that the value was possibly modified by an instruction
99+
/// that was not evaluated during the interpretation.
100+
MutatedByUnevaluatedInstruction,
93101
};
94102

95103
/// An abstract class that exposes functions for allocating symbolic values.
@@ -416,6 +424,8 @@ class SymbolicValue {
416424
/// reason, we fall back to using the specified location.
417425
void emitUnknownDiagnosticNotes(SILLocation fallbackLoc);
418426

427+
bool isUnknownDueToUnevaluatedInstructions();
428+
419429
/// Clone this SymbolicValue into the specified Allocator and return the new
420430
/// version. This only works for valid constants.
421431
SymbolicValue cloneInto(SymbolicValueAllocator &allocator) const;

branches/master-rebranch/include/swift/SILOptimizer/Utils/ConstExpr.h

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class ConstExprEvaluator {
8585

8686
/// A constant-expression evaluator that can be used to step through a control
8787
/// flow graph (SILFunction body) by evaluating one instruction at a time.
88+
/// This evaluator can also "skip" instructions without evaluating them and
89+
/// only track constant values of variables whose values could be computed.
8890
class ConstExprStepEvaluator {
8991
private:
9092
ConstExprEvaluator evaluator;
@@ -97,10 +99,6 @@ class ConstExprStepEvaluator {
9799
/// evaluation.
98100
SmallPtrSet<SILBasicBlock *, 8> visitedBlocks;
99101

100-
Optional<SymbolicValue>
101-
incrementStepsAndCheckLimit(SILInstruction *inst,
102-
bool includeInInstructionLimit);
103-
104102
ConstExprStepEvaluator(const ConstExprEvaluator &) = delete;
105103
void operator=(const ConstExprEvaluator &) = delete;
106104

@@ -122,12 +120,61 @@ class ConstExprStepEvaluator {
122120
/// Second element is None, if the evaluation is successful.
123121
/// Otherwise, is an unknown symbolic value that contains the error.
124122
std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>>
125-
evaluate(SILBasicBlock::iterator instI,
126-
bool includeInInstructionLimit = true);
123+
evaluate(SILBasicBlock::iterator instI);
124+
125+
/// Skip the instruction without evaluating it and conservatively account for
126+
/// the effects of the instruction on the internal state. This operation
127+
/// resets to an unknown symbolic value any portion of a
128+
/// SymbolicValueMemoryObject that could possibly be mutated by the given
129+
/// instruction. This function preserves the soundness of the interpretation.
130+
/// \param instI instruction to be skipped.
131+
/// \returns a pair where the first and second elements are defined as
132+
/// follows:
133+
/// The first element, if is not None, is the iterator to the next
134+
/// instruction from the where the evaluation must continue.
135+
/// The first element is None if the next instruction from where the
136+
/// evaluation must continue cannot be determined.
137+
/// This would be the case if `instI` is a branch like a `condbr`.
138+
///
139+
/// Second element is None if skipping the instruction is successful.
140+
/// Otherwise, it is an unknown symbolic value containing the error.
141+
std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>>
142+
skipByMakingEffectsNonConstant(SILBasicBlock::iterator instI);
143+
144+
/// Try evaluating an instruction and if the evaluation fails, skip the
145+
/// instruction and make it effects non constant. Note that it may not always
146+
/// be possible to skip an instruction whose evaluation failed and
147+
/// continue evalution (e.g. a conditional branch).
148+
/// See `evaluate` and `skipByMakingEffectsNonConstant` functions for their
149+
/// semantics.
150+
/// \param instI instruction to be evaluated in the current interpreter state.
151+
/// \returns a pair where the first and second elements are defined as
152+
/// follows:
153+
/// The first element, if is not None, is the iterator to the next
154+
/// instruction from the where the evaluation must continue.
155+
/// The first element is None iff both `evaluate` and `skip` functions
156+
/// failed to determine the next instruction to continue evaluation from.
157+
///
158+
/// Second element is None if the evaluation is successful.
159+
/// Otherwise, it is an unknown symbolic value containing the error.
160+
std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>>
161+
tryEvaluateOrElseMakeEffectsNonConstant(SILBasicBlock::iterator instI);
127162

128163
Optional<SymbolicValue> lookupConstValue(SILValue value);
129164

130165
bool isKnownFunction(SILFunction *fun);
166+
167+
/// Returns true if and only if `errorVal` denotes an error that requires
168+
/// aborting interpretation and returning the error. Skipping an instruction
169+
/// that produces such errors is not a valid behavior.
170+
bool isFailStopError(SymbolicValue errorVal);
171+
172+
/// Return the number of instructions evaluated for the last `evaluate`
173+
/// operation. This could be used by the clients to limit the number of
174+
/// instructions that should be evaluated by the step-wise evaluator.
175+
/// Note that 'skipByMakingEffectsNonConstant' operation is not considered
176+
/// as an evaluation.
177+
unsigned instructionsEvaluatedByLastEvaluation() { return stepsEvaluated; }
131178
};
132179

133180
} // end namespace swift

branches/master-rebranch/lib/SIL/SILConstants.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,12 @@ SymbolicValue SymbolicValue::lookThroughSingleElementAggregates() const {
568568
}
569569
}
570570

571+
bool SymbolicValue::isUnknownDueToUnevaluatedInstructions() {
572+
auto unknownReason = getUnknownReason();
573+
return (unknownReason == UnknownReason::ReturnedByUnevaluatedInstruction ||
574+
unknownReason == UnknownReason::MutatedByUnevaluatedInstruction);
575+
}
576+
571577
/// Given that this is an 'Unknown' value, emit diagnostic notes providing
572578
/// context about what the problem is. Specifically, point to interesting
573579
/// source locations and function calls in the call stack.
@@ -676,6 +682,12 @@ void SymbolicValue::emitUnknownDiagnosticNotes(SILLocation fallbackLoc) {
676682
if (emitTriggerLocInDiag)
677683
diagnose(ctx, *triggerLoc, diag::constexpr_witness_call_found_here);
678684
return;
685+
case UnknownReason::ReturnedByUnevaluatedInstruction:
686+
diagnose(ctx, diagLoc, diag::constexpr_returned_by_unevaluated_instruction);
687+
break;
688+
case UnknownReason::MutatedByUnevaluatedInstruction:
689+
diagnose(ctx, diagLoc, diag::constexpr_mutated_by_unevaluated_instruction);
690+
break;
679691
}
680692
// TODO: print the call-stack in a controlled way if needed.
681693
}

branches/master-rebranch/lib/SILOptimizer/UtilityPasses/ConstantEvaluatorTester.cpp

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#define DEBUG_TYPE "sil-constant-evaluation-tester"
14+
#include "swift/AST/DiagnosticsSIL.h"
1415
#include "swift/SIL/SILConstants.h"
1516
#include "swift/SILOptimizer/PassManager/Passes.h"
1617
#include "swift/SILOptimizer/PassManager/Transforms.h"
@@ -20,6 +21,12 @@ using namespace swift;
2021

2122
namespace {
2223

24+
template <typename... T, typename... U>
25+
static InFlightDiagnostic diagnose(ASTContext &Context, SourceLoc loc,
26+
Diag<T...> diag, U &&... args) {
27+
return Context.Diags.diagnose(loc, diag, std::forward<U>(args)...);
28+
}
29+
2330
/// A compiler pass for testing constant evaluator in the step-wise evaluation
2431
/// mode. The pass evaluates SIL functions whose names start with "interpret"
2532
/// and outputs the constant value returned by the function or diagnostics if
@@ -31,6 +38,18 @@ class ConstantEvaluatorTester : public SILFunctionTransform {
3138
return fun->getName().startswith("interpret");
3239
}
3340

41+
bool shouldSkipInstruction(SILInstruction *inst) {
42+
auto *applyInst = dyn_cast<ApplyInst>(inst);
43+
if (!applyInst)
44+
return false;
45+
46+
auto *callee = applyInst->getReferencedFunction();
47+
if (!callee)
48+
return false;
49+
50+
return callee->getName().startswith("skip");
51+
}
52+
3453
void run() override {
3554
SILFunction *fun = getFunction();
3655

@@ -52,6 +71,7 @@ class ConstantEvaluatorTester : public SILFunctionTransform {
5271
if (!returnVal) {
5372
llvm::errs() << "Returns unknown"
5473
<< "\n";
74+
break;
5575
}
5676
llvm::errs() << "Returns " << returnVal.getValue() << "\n";
5777
break;
@@ -60,15 +80,33 @@ class ConstantEvaluatorTester : public SILFunctionTransform {
6080
Optional<SILBasicBlock::iterator> nextInstOpt;
6181
Optional<SymbolicValue> errorVal;
6282

63-
std::tie(nextInstOpt, errorVal) = stepEvaluator.evaluate(currI);
64-
if (errorVal.hasValue()) {
65-
// Diagnose the error.
66-
assert(errorVal->getKind() == SymbolicValue::Unknown);
83+
// If the instruction is marked as skip, skip it and make its effects
84+
// non-constant. Otherwise, try evaluating the instruction and if the
85+
// evaluation fails due to a previously skipped instruction,
86+
// skip the current instruction.
87+
if (shouldSkipInstruction(inst)) {
88+
std::tie(nextInstOpt, errorVal) =
89+
stepEvaluator.skipByMakingEffectsNonConstant(currI);
90+
} else {
91+
std::tie(nextInstOpt, errorVal) =
92+
stepEvaluator.tryEvaluateOrElseMakeEffectsNonConstant(currI);
93+
}
94+
95+
// Diagnose errors in the evaluation. Unknown symbolic values produced
96+
// by skipping instructions are not considered errors.
97+
if (errorVal.hasValue() &&
98+
!errorVal->isUnknownDueToUnevaluatedInstructions()) {
99+
errorVal->emitUnknownDiagnosticNotes(inst->getLoc());
100+
break;
101+
}
102+
103+
if (!nextInstOpt) {
104+
diagnose(fun->getASTContext(), inst->getLoc().getSourceLoc(),
105+
diag::constexpr_unknown_control_flow_due_to_skip);
67106
errorVal->emitUnknownDiagnosticNotes(inst->getLoc());
68107
break;
69108
}
70109

71-
assert(nextInstOpt);
72110
currI = nextInstOpt.getValue();
73111
}
74112
}

0 commit comments

Comments
 (0)