Skip to content

Commit 09299de

Browse files
authored
Merge pull request #27617 from ravikandhadai/constexpr-toplevel-separation
2 parents 37afa7c + 28e4cd0 commit 09299de

File tree

4 files changed

+59
-47
lines changed

4 files changed

+59
-47
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ NOTE(constexpr_witness_call_with_no_conformance, none,
422422
NOTE(constexpr_unknown_control_flow_due_to_skip,none, "branch depends on "
423423
"non-constant value produced by an unevaluated instructions", ())
424424
NOTE(constexpr_returned_by_unevaluated_instruction,none,
425-
"return value of an unevaluated instruction is not a constant", ())
425+
"result of an unevaluated instruction is not a constant", ())
426426
NOTE(constexpr_mutated_by_unevaluated_instruction,none, "value mutable by an "
427427
"unevaluated instruction is not a constant", ())
428428

lib/SILOptimizer/Utils/ConstExpr.cpp

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,22 @@ class ConstExprFunctionState {
123123
/// addresses.
124124
llvm::DenseMap<SILValue, SymbolicValue> calculatedValues;
125125

126+
/// If a SILValue is not bound to a SymbolicValue in the calculatedValues,
127+
/// try to compute it recursively by visiting its defining instruction.
128+
bool recursivelyComputeValueIfNotInState = false;
129+
126130
public:
127131
ConstExprFunctionState(ConstExprEvaluator &evaluator, SILFunction *fn,
128132
SubstitutionMap substitutionMap,
129-
unsigned &numInstEvaluated)
133+
unsigned &numInstEvaluated,
134+
bool enableTopLevelEvaluation)
130135
: evaluator(evaluator), fn(fn), substitutionMap(substitutionMap),
131-
numInstEvaluated(numInstEvaluated) {}
136+
numInstEvaluated(numInstEvaluated),
137+
recursivelyComputeValueIfNotInState(enableTopLevelEvaluation) {
138+
assert((!fn || !enableTopLevelEvaluation) &&
139+
"top-level evaluation must be disabled when evaluating a function"
140+
" body step by step");
141+
}
132142

133143
/// Pretty print the state to stderr.
134144
void dump() const {
@@ -168,8 +178,9 @@ class ConstExprFunctionState {
168178
return result;
169179
}
170180

171-
/// Return the SymbolicValue for the specified SIL value, lazily computing
172-
/// it if needed.
181+
/// Return the SymbolicValue for the specified SIL value. If the SIL value is
182+
/// not in \c calculatedValues, try computing the SymbolicValue recursively
183+
/// if \c recursivelyComputeValueIfNotInState flag is set.
173184
SymbolicValue getConstantValue(SILValue value);
174185

175186
/// Evaluate the specified instruction in a flow sensitive way, for use by
@@ -232,14 +243,6 @@ Type ConstExprFunctionState::substituteGenericParamsAndSimpify(Type ty) {
232243
SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) {
233244
assert(!calculatedValues.count(value));
234245

235-
// If the client is asking for the value of a stack object that hasn't been
236-
// computed, and if fn is null, then we are in top level code, and the
237-
// stack object must be a single store value. Since this is a very different
238-
// computation, split it out to its own path.
239-
if (!fn && value->getType().isAddress() && isa<AllocStackInst>(value)) {
240-
return getSingleWriterAddressValue(value);
241-
}
242-
243246
// If this a trivial constant instruction that we can handle, then fold it
244247
// immediately.
245248
if (auto *ili = dyn_cast<IntegerLiteralInst>(value))
@@ -382,17 +385,6 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) {
382385
if (auto *builtin = dyn_cast<BuiltinInst>(value))
383386
return computeConstantValueBuiltin(builtin);
384387

385-
if (auto *apply = dyn_cast<ApplyInst>(value)) {
386-
auto callResult = computeCallResult(apply);
387-
388-
// If this failed, return the error code.
389-
if (callResult.hasValue())
390-
return callResult.getValue();
391-
392-
assert(calculatedValues.count(apply));
393-
return calculatedValues[apply];
394-
}
395-
396388
if (auto *enumVal = dyn_cast<EnumInst>(value)) {
397389
if (!enumVal->hasOperand())
398390
return SymbolicValue::getEnum(enumVal->getElement());
@@ -1174,15 +1166,37 @@ ConstExprFunctionState::computeCallResult(ApplyInst *apply) {
11741166
return None;
11751167
}
11761168

1177-
/// Return the SymbolicValue for the specified SIL value, lazily computing
1178-
/// it if needed.
11791169
SymbolicValue ConstExprFunctionState::getConstantValue(SILValue value) {
11801170
// Check to see if we already have an answer.
11811171
auto it = calculatedValues.find(value);
11821172
if (it != calculatedValues.end())
11831173
return it->second;
11841174

1185-
// Compute the value of a normal instruction based on its operands.
1175+
if (!recursivelyComputeValueIfNotInState) {
1176+
return getUnknown(evaluator, value, UnknownReason::UntrackedSILValue);
1177+
}
1178+
1179+
// If the client is asking for the value of a stack object that hasn't been
1180+
// computed, and if we have to recursively compute it, the stack object must
1181+
// be a single store value. Since this is a very different computation,
1182+
// split it out to its own path.
1183+
if (value->getType().isAddress() && isa<AllocStackInst>(value)) {
1184+
return getSingleWriterAddressValue(value);
1185+
}
1186+
1187+
if (auto *apply = dyn_cast<ApplyInst>(value)) {
1188+
auto callResult = computeCallResult(apply);
1189+
1190+
// If this failed, return the error code.
1191+
if (callResult.hasValue())
1192+
return callResult.getValue();
1193+
1194+
assert(calculatedValues.count(apply));
1195+
return calculatedValues[apply];
1196+
}
1197+
1198+
// Compute the value of a normal single-value instructions based on its
1199+
// operands.
11861200
auto result = computeConstantValue(value);
11871201

11881202
// If this is the top-level lazy interpreter, output a debug trace.
@@ -1567,15 +1581,6 @@ ConstExprFunctionState::evaluateFlowSensitive(SILInstruction *inst) {
15671581
return None;
15681582
}
15691583

1570-
// Make sure that our copy_value, begin_borrow form constants. Otherwise,
1571-
// return why.
1572-
if (isa<CopyValueInst>(inst) || isa<BeginBorrowInst>(inst)) {
1573-
auto result = getConstantValue(inst->getOperand(0));
1574-
if (!result.isConstant())
1575-
return result;
1576-
return None;
1577-
}
1578-
15791584
// If this is a deallocation of a memory object that we are tracking, then
15801585
// don't do anything. The memory is allocated in a BumpPtrAllocator so there
15811586
// is no useful way to free it.
@@ -1596,7 +1601,10 @@ ConstExprFunctionState::evaluateFlowSensitive(SILInstruction *inst) {
15961601
}
15971602
}
15981603

1599-
// If this is a call, evaluate it.
1604+
// If this is a call, evaluate it. Calls are handled separately from other
1605+
// single-valued instructions because calls which return void will not be
1606+
// mapped to a symbolic value. Every other single-valued instruction will be
1607+
// mapped to a symbolic value if its evaluation is successful.
16001608
if (auto apply = dyn_cast<ApplyInst>(inst))
16011609
return computeCallResult(apply);
16021610

@@ -1622,13 +1630,13 @@ ConstExprFunctionState::evaluateFlowSensitive(SILInstruction *inst) {
16221630
injectEnumInst->getOperand());
16231631
}
16241632

1625-
// If the instruction produces a result, try constant folding it.
1626-
// If this fails, then we fail.
1627-
if (isa<SingleValueInstruction>(inst)) {
1628-
auto oneResultVal = inst->getResults()[0];
1629-
auto result = getConstantValue(oneResultVal);
1633+
// If the instruction produces a result, try computing it, and fail if the
1634+
// computation fails.
1635+
if (auto *singleValueInst = dyn_cast<SingleValueInstruction>(inst)) {
1636+
auto result = computeConstantValue(singleValueInst);
16301637
if (!result.isConstant())
16311638
return result;
1639+
setValue(singleValueInst, result);
16321640
LLVM_DEBUG(llvm::dbgs() << " RESULT: "; result.dump());
16331641
return None;
16341642
}
@@ -1767,7 +1775,8 @@ evaluateAndCacheCall(SILFunction &fn, SubstitutionMap substitutionMap,
17671775
ConstExprEvaluator &evaluator) {
17681776
assert(!fn.isExternalDeclaration() && "Can't analyze bodyless function");
17691777
ConstExprFunctionState state(evaluator, &fn, substitutionMap,
1770-
numInstEvaluated);
1778+
numInstEvaluated,
1779+
/*TopLevelEvaluation*/ false);
17711780

17721781
// TODO: implement caching.
17731782
// TODO: reject code that is too complex.
@@ -1860,7 +1869,9 @@ SymbolicValue ConstExprEvaluator::getUnknown(SILNode *node,
18601869
void ConstExprEvaluator::computeConstantValues(
18611870
ArrayRef<SILValue> values, SmallVectorImpl<SymbolicValue> &results) {
18621871
unsigned numInstEvaluated = 0;
1863-
ConstExprFunctionState state(*this, nullptr, {}, numInstEvaluated);
1872+
ConstExprFunctionState state(*this, /*SILFunction*/ nullptr, {},
1873+
numInstEvaluated,
1874+
/*enableTopLevelEvaluation*/ true);
18641875
for (auto v : values) {
18651876
auto symVal = state.getConstantValue(v);
18661877
results.push_back(symVal);
@@ -1881,7 +1892,8 @@ ConstExprStepEvaluator::ConstExprStepEvaluator(SymbolicValueAllocator &alloc,
18811892
bool trackCallees)
18821893
: evaluator(alloc, assertConf, trackCallees),
18831894
internalState(
1884-
new ConstExprFunctionState(evaluator, fun, {}, stepsEvaluated)) {
1895+
new ConstExprFunctionState(evaluator, fun, {}, stepsEvaluated,
1896+
/*enableTopLevelEvaluation*/ false)) {
18851897
assert(fun);
18861898
}
18871899

test/SILOptimizer/constant_evaluator_skip_test.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ bb0:
9393
%10 = builtin "cmp_slt_Int32"(%8 : $Builtin.Int32, %9 : $Builtin.Int32) : $Builtin.Int1
9494
cond_br %10, bb2, bb3
9595
// CHECK: {{.*}}:[[@LINE-1]]:{{.*}}: note: branch depends on non-constant value produced by an unevaluated instructions
96-
// CHECK: {{.*}}: note: value mutable by an unevaluated instruction is not a constant
96+
// CHECK: {{.*}}: note: result of an unevaluated instruction is not a constant
9797
bb2:
9898
br bb4
9999

test/SILOptimizer/constant_evaluator_test.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ bb0:
449449
sil hidden @interpretAndDiagnoseNonConstantVars : $@convention(thin) (Int) -> () {
450450
bb0(%0 : $Int):
451451
%4 = struct_extract %0 : $Int, #Int._value
452-
// CHECK: {{.*}}:[[@LINE-1]]:{{.*}}: note: cannot evaluate expression as constant here
452+
// CHECK: {{.*}}:[[@LINE-1]]:{{.*}}: note: encountered use of a variable not tracked by the evaluator
453453
%10 = tuple ()
454454
return %10 : $()
455455
}

0 commit comments

Comments
 (0)