Skip to content

Commit 89878a7

Browse files
authored
Merge pull request #40254 from atrick/fix-dead-stdlib-assert
Add isReadOnlyConstantEvaluableCall to handle stdlib asserts
2 parents fccbaa8 + 0712df9 commit 89878a7

File tree

4 files changed

+81
-23
lines changed

4 files changed

+81
-23
lines changed

include/swift/SILOptimizer/Utils/ConstExpr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include "swift/Basic/LLVM.h"
2828
#include "swift/Basic/SourceLoc.h"
29+
#include "swift/SIL/ApplySite.h"
2930
#include "swift/SIL/SILBasicBlock.h"
3031
#include "llvm/ADT/SmallPtrSet.h"
3132

@@ -211,6 +212,9 @@ bool hasConstantEvaluableAnnotation(SILFunction *fun);
211212

212213
bool isConstantEvaluable(SILFunction *fun);
213214

215+
/// Return true iff the \p applySite is constant-evaluable and read-only.
216+
bool isReadOnlyConstantEvaluableCall(FullApplySite applySite);
217+
214218
/// Return true if and only if the given function \p fun is specially modeled
215219
/// by the constant evaluator. These are typically functions in the standard
216220
/// library, such as String.+=, Array.append, whose semantics is built into the

lib/SILOptimizer/Utils/ConstExpr.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,27 @@ static llvm::Optional<WellKnownFunction> classifyFunction(SILFunction *fn) {
111111
return None;
112112
}
113113

114+
static bool isReadOnlyFunction(WellKnownFunction function) {
115+
switch (function) {
116+
case WellKnownFunction::ArrayInitEmpty:
117+
case WellKnownFunction::AllocateUninitializedArray:
118+
case WellKnownFunction::StringInitEmpty:
119+
case WellKnownFunction::StringMakeUTF8:
120+
case WellKnownFunction::StringEquals:
121+
case WellKnownFunction::StringEscapePercent:
122+
case WellKnownFunction::BinaryIntegerDescription:
123+
return true;
124+
125+
case WellKnownFunction::EndArrayMutation:
126+
case WellKnownFunction::FinalizeUninitializedArray:
127+
case WellKnownFunction::ArrayAppendElement:
128+
case WellKnownFunction::StringAppend:
129+
case WellKnownFunction::AssertionFailure:
130+
case WellKnownFunction::DebugPrint:
131+
return false;
132+
}
133+
}
134+
114135
/// Helper function for creating UnknownReason without a payload.
115136
static SymbolicValue getUnknown(ConstExprEvaluator &evaluator, SILNode *node,
116137
UnknownReason::UnknownKind kind) {
@@ -2291,3 +2312,39 @@ bool swift::isConstantEvaluable(SILFunction *fun) {
22912312
return hasConstantEvaluableAnnotation(fun) ||
22922313
isKnownConstantEvaluableFunction(fun);
22932314
}
2315+
2316+
/// Return true iff the \p applySite is constant-evaluable and read-only.
2317+
///
2318+
/// Functions annotated as "constant_evaluable" are assumed to be "side-effect
2319+
/// free", unless their signature and substitution map indicates otherwise. A
2320+
/// constant_evaluable function call is read only unless it:
2321+
/// (1) has generic parameters
2322+
/// (2) has inout parameters
2323+
/// (3) has indirect results
2324+
///
2325+
/// Read-only constant evaluable functions can do only the following and
2326+
/// nothing else:
2327+
/// (1) The call may read any memory location.
2328+
/// (2) The call may destroy owned parameters i.e., consume them.
2329+
/// (3) The call may write into memory locations newly created by the call.
2330+
/// (4) The call may use assertions, which traps at runtime on failure.
2331+
/// (5) The call may return a non-generic value.
2332+
///
2333+
/// Essentially, these are calls whose "effect" is visible only in their return
2334+
/// value or through the parameters that are destroyed. The return value
2335+
/// is also guaranteed to have value semantics as it is non-generic and
2336+
/// reference semantics is not constant evaluable.
2337+
bool swift::isReadOnlyConstantEvaluableCall(FullApplySite applySite) {
2338+
SILFunction *callee = applySite.getCalleeFunction();
2339+
if (!callee)
2340+
return false;
2341+
2342+
if (auto knownFunction = classifyFunction(callee)) {
2343+
return isReadOnlyFunction(knownFunction.getValue());
2344+
}
2345+
if (!hasConstantEvaluableAnnotation(callee))
2346+
return false;
2347+
2348+
return !applySite.hasSubstitutions() && !getNumInOutArguments(applySite)
2349+
&& !applySite.getNumIndirectSILResults();
2350+
}

lib/SILOptimizer/Utils/InstructionDeleter.cpp

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,6 @@
1818

1919
using namespace swift;
2020

21-
/// Return true iff the \p applySite calls a constant-evaluable function and
22-
/// it is non-generic and read/destroy only, which means that the call can do
23-
/// only the following and nothing else:
24-
/// (1) The call may read any memory location.
25-
/// (2) The call may destroy owned parameters i.e., consume them.
26-
/// (3) The call may write into memory locations newly created by the call.
27-
/// (4) The call may use assertions, which traps at runtime on failure.
28-
/// (5) The call may return a non-generic value.
29-
/// Essentially, these are calls whose "effect" is visible only in their return
30-
/// value or through the parameters that are destroyed. The return value
31-
/// is also guaranteed to have value semantics as it is non-generic and
32-
/// reference semantics is not constant evaluable.
33-
static bool isNonGenericReadOnlyConstantEvaluableCall(FullApplySite applySite) {
34-
assert(applySite);
35-
SILFunction *callee = applySite.getCalleeFunction();
36-
if (!callee || !isConstantEvaluable(callee)) {
37-
return false;
38-
}
39-
return !applySite.hasSubstitutions() && !getNumInOutArguments(applySite) &&
40-
!applySite.getNumIndirectSILResults();
41-
}
42-
4321
static bool hasOnlyIncidentalUses(SILInstruction *inst,
4422
bool disallowDebugUses = false) {
4523
for (SILValue result : inst->getResults()) {
@@ -160,7 +138,7 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst,
160138
// provided the parameter lifetimes are handled correctly, which is taken
161139
// care of by the function: \c deleteInstruction.
162140
FullApplySite applySite(cast<ApplyInst>(inst));
163-
return isNonGenericReadOnlyConstantEvaluableCall(applySite);
141+
return isReadOnlyConstantEvaluableCall(applySite);
164142
}
165143
default: {
166144
return false;

test/SILOptimizer/sil_combine_ossa.sil

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5219,3 +5219,22 @@ bb0(%0 : @owned $Klass, %1 : $Builtin.Word):
52195219
destroy_value %0 : $Klass
52205220
return %11 : $AnyObject
52215221
}
5222+
5223+
// Test that DCE during SILCombine does not remove traps.
5224+
// Tests isReadOnlyConstantEvaluable().
5225+
sil hidden [noinline] [_semantics "programtermination_point"] @trapWrapper : $@convention(thin) () -> () {
5226+
bb0:
5227+
%0 = builtin "int_trap"() : $Never
5228+
unreachable
5229+
}
5230+
5231+
// CHECK-LABEL: sil [ossa] @testTrap : $@convention(thin) () -> () {
5232+
// CHECK: apply
5233+
// CHECK-LABEL: } // end sil function 'testTrap'
5234+
sil [ossa] @testTrap : $@convention(thin) () -> () {
5235+
bb0:
5236+
%0 = function_ref @trapWrapper : $@convention(thin) () -> ()
5237+
%1 = apply %0() : $@convention(thin) () -> ()
5238+
%2 = tuple ()
5239+
return %2 : $()
5240+
}

0 commit comments

Comments
 (0)