Skip to content

Commit b59cc7a

Browse files
committed
[Constant Evaluator] Add support to the constant evaluator for:
1. builtin "int_expect", which makes the evaluator work on more integer operations such as left/right shift (with traps) and integer conversions. 2. builtin "_assertConfiguration", which enables the evaluator to work with debug stdlib. 3. builtin "ptrtoint", which enables the evaluator to track StaticString 4. _assertionFailure API, which enables the evaluator to report stdlib assertion failures encountered during constant evaluation. Also, enable attaching auxiliary data with the enum "UnknownReason" and use it to improve diagnostics for UnknownSymbolicValues, which represent failures during constant evaluation.
1 parent bf1ab6c commit b59cc7a

File tree

11 files changed

+473
-213
lines changed

11 files changed

+473
-213
lines changed

include/swift/AST/Builtins.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ BUILTIN_BINARY_OPERATION(ExactUDiv, "udiv_exact", "n", IntegerOrVector)
7575
BUILTIN_BINARY_OPERATION(URem, "urem", "n", Integer)
7676
BUILTIN_BINARY_OPERATION(FRem, "frem", "n", FloatOrVector)
7777
BUILTIN_BINARY_OPERATION(Xor, "xor", "n", IntegerOrVector)
78+
// This builtin is an optimizer hint and always returns the first argument.
79+
BUILTIN_BINARY_OPERATION(Expect, "int_expect", "n", Integer)
7880
#undef BUILTIN_BINARY_OPERATION
7981

8082
/// These builtins are analogous the similarly named llvm intrinsics. The

include/swift/AST/DiagnosticsSIL.def

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -360,61 +360,75 @@ ERROR(pound_assert_failure,none,
360360
NOTE(constexpr_unknown_reason_default,none,
361361
"cannot evaluate expression as constant here", ())
362362
NOTE(constexpr_unevaluable_operation,none,
363-
"cannot constant evaluate operation", ())
363+
"cannot constant evaluate operation%select{| used by this call}0", (bool))
364364

365365
NOTE(constexpr_too_many_instructions,none,
366366
"exceeded instruction limit: %0 when evaluating the expression "
367367
"at compile time", (unsigned))
368-
NOTE(constexpr_limit_exceeding_instruction,none, "limit exceeded here", ())
368+
NOTE(constexpr_limit_exceeding_instruction,none, "limit exceeded "
369+
"%select{here|during this call}0", (bool))
369370

370371
NOTE(constexpr_loop_found_note,none,
371372
"control-flow loop found during evaluation ", ())
372-
NOTE(constexpr_loop_instruction,none, "found loop here", ())
373+
NOTE(constexpr_loop_instruction,none, "found loop "
374+
"%select{here|inside this call}0", (bool))
373375

374376
NOTE(constexpr_overflow,none, "integer overflow detected", ())
375-
NOTE(constexpr_overflow_operation,none, "operation overflows", ())
377+
NOTE(constexpr_overflow_operation,none, "operation"
378+
"%select{| performed during this call}0 overflows", (bool))
376379

377380
NOTE(constexpr_trap,none, "trap detected", ())
378-
NOTE(constexpr_trap_operation,none, "operation traps", ())
381+
NOTE(constexpr_trap_operation,none, "operation"
382+
"%select{| performed during this call}0 traps", (bool))
383+
384+
NOTE(constexpr_assertion_failed, none, "assertion failed with message: %0",
385+
(StringRef))
386+
NOTE(constexpr_assertion_failed_here, none, "assertion failed"
387+
"%select{ here| during this call}0 ", (bool))
379388

380389
NOTE(constexpr_invalid_operand_seen, none,
381390
"operation with invalid operands encountered during evaluation",())
382391
NOTE(constexpr_operand_invalid_here, none,
383-
"operation with invalid operands encountered here",())
392+
"operation with invalid operands encountered "
393+
"%select{here|during this call}0", (bool))
384394

385395
NOTE(constexpr_value_unknown_at_top_level,none,
386396
"cannot evaluate top-level value as constant here",())
387397
NOTE(constexpr_multiple_writers_found_at_top_level,none,
388398
"top-level value has multiple assignments",())
389399

390400
NOTE(constexpr_unsupported_instruction_found, none,
391-
"encountered operation not supported by the evaluator", ())
392-
NOTE(constexpr_unsupported_instruction_found_here,none,
393-
"operation not supported by the evaluator", ())
401+
"encountered operation not supported by the evaluator: %0", (StringRef))
402+
NOTE(constexpr_unsupported_instruction_found_here,none, "operation"
403+
"%select{| used by this call is}0 not supported by the evaluator", (bool))
394404

395405
NOTE(constexpr_unknown_function_called, none,
396-
"encountered call to a function whose body is not available", ())
406+
"encountered call to '%0' whose body is not available", (StringRef))
397407
NOTE(constexpr_unknown_function_called_here, none,
398-
"call to a function whose body is not available", ())
408+
"%select{|calls a }0function whose body is not available", (bool))
399409

400410
NOTE(constexpr_untracked_sil_value_use_found, none,
401411
"encountered use of a variable not tracked by the evaluator", ())
402412
NOTE(constexpr_untracked_sil_value_used_here, none,
403-
"untracked variable used here", ())
404-
405-
NOTE(constexpr_witness_call_with_no_conformance_found, none,
406-
"cannot find concrete conformance for a witness method call", ())
407-
NOTE(constexpr_witness_call_with_no_target_found, none,
408-
"cannot resolve a witness method call to a concrete function", ())
409-
NOTE(constexpr_witness_call_found_here, none,
410-
"witness method call found here", ())
413+
"untracked variable used %select{here|by this call}0", (bool))
414+
415+
NOTE(constexpr_unresolvable_witness_call, none,
416+
"encountered unresolvable witness method call: '%0'", (StringRef))
417+
NOTE(constexpr_no_witness_table_entry, none, "cannot find witness table entry "
418+
"%select{for this call|for a witness-method invoked during this call}0",
419+
(bool))
420+
NOTE(constexpr_witness_call_with_no_conformance, none,
421+
"cannot find concrete conformance "
422+
"%select{for this call|for a witness-method invoked during this call}0",
423+
(bool))
411424

412425
NOTE(constexpr_unknown_control_flow_due_to_skip,none, "branch depends on "
413426
"non-constant value produced by an unevaluated instructions", ())
414427
NOTE(constexpr_returned_by_unevaluated_instruction,none,
415428
"return value of an unevaluated instruction is not a constant", ())
416429
NOTE(constexpr_mutated_by_unevaluated_instruction,none, "value mutable by an "
417430
"unevaluated instruction is not a constant", ())
431+
ERROR(not_constant_evaluable, none, "not constant evaluable", ())
418432

419433
ERROR(non_physical_addressof,none,
420434
"addressof only works with purely physical lvalues; "

include/swift/SIL/SILConstants.h

Lines changed: 99 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -41,63 +41,119 @@ extern llvm::cl::opt<unsigned> ConstExprLimit;
4141
/// allowing the caller to produce a specific diagnostic. The "Unknown"
4242
/// SymbolicValue representation also includes a pointer to the SILNode in
4343
/// question that was problematic.
44-
enum class UnknownReason {
45-
// TODO: Eliminate the default code, by making classifications for each
46-
// failure mode.
47-
Default,
44+
class UnknownReason {
45+
public:
46+
enum UnknownKind {
47+
// TODO: Eliminate the default kind, by making classifications for each
48+
// failure mode.
49+
Default,
50+
51+
/// The constant expression was too big. This is reported on a random
52+
/// instruction within the constexpr that triggered the issue.
53+
TooManyInstructions,
54+
55+
/// A control flow loop was found.
56+
Loop,
57+
58+
/// Integer overflow detected.
59+
Overflow,
60+
61+
/// Unspecified trap detected.
62+
Trap,
63+
64+
/// Assertion failure detected. These have an associated message unlike
65+
/// traps.
66+
AssertionFailure,
4867

49-
/// The constant expression was too big. This is reported on a random
50-
/// instruction within the constexpr that triggered the issue.
51-
TooManyInstructions,
68+
/// An operation was applied over operands whose symbolic values were
69+
/// constants but were not valid for the operation.
70+
InvalidOperandValue,
5271

53-
/// A control flow loop was found.
54-
Loop,
72+
/// Encountered an instruction not supported by the interpreter.
73+
UnsupportedInstruction,
5574

56-
/// Integer overflow detected.
57-
Overflow,
75+
/// Encountered a function call where the body of the called function is
76+
/// not available.
77+
CalleeImplementationUnknown,
5878

59-
/// Unspecified trap detected.
60-
Trap,
79+
/// Attempted to load from/store into a SIL value that was not tracked by
80+
/// the interpreter.
81+
UntrackedSILValue,
6182

62-
/// An operation was applied over operands whose symbolic values were
63-
/// constants but were not valid for the operation.
64-
InvalidOperandValue,
83+
/// Attempted to find a concrete protocol conformance for a witness method
84+
/// and failed.
85+
UnknownWitnessMethodConformance,
6586

66-
/// Encountered an instruction not supported by the interpreter.
67-
UnsupportedInstruction,
87+
/// Attempted to determine the SIL function of a witness method and failed.
88+
NoWitnesTableEntry,
6889

69-
/// Encountered a function call where the body of the called function is
70-
/// not available.
71-
CalleeImplementationUnknown,
90+
/// The value of a top-level variable cannot be determined to be a constant.
91+
/// This is only relevant in the backward evaluation mode, which is used by
92+
/// #assert.
93+
NotTopLevelConstant,
7294

73-
/// Attempted to load from/store into a SIL value that was not tracked by
74-
/// the interpreter.
75-
UntrackedSILValue,
95+
/// A top-level value has multiple writers. This is only relevant in the
96+
/// non-flow-sensitive evaluation mode, which is used by #assert.
97+
MutipleTopLevelWriters,
7698

77-
/// Attempted to find a concrete protocol conformance for a witness method
78-
/// and failed.
79-
UnknownWitnessMethodConformance,
99+
/// Indicates the return value of an instruction that was not evaluated
100+
/// during interpretation.
101+
ReturnedByUnevaluatedInstruction,
80102

81-
/// Attempted to determine the SIL function of a witness method (based on a
82-
/// concrete protocol conformance) and failed.
83-
UnresolvableWitnessMethod,
103+
/// Indicates that the value was possibly modified by an instruction
104+
/// that was not evaluated during the interpretation.
105+
MutatedByUnevaluatedInstruction,
106+
};
84107

85-
/// The value of a top-level variable cannot be determined to be a constant.
86-
/// This is only relevant in the backward evaluation mode, which is used by
87-
/// #assert.
88-
NotTopLevelConstant,
108+
private:
109+
UnknownKind kind;
89110

90-
/// A top-level value has multiple writers. This is only relevant in the
91-
/// non-flow-sensitive evaluation mode, which is used by #assert.
92-
MutipleTopLevelWriters,
111+
// Auxiliary information for different unknown kinds.
112+
union {
113+
SILFunction *function;
114+
const char *failedAssertMessage;
115+
} payload;
93116

94-
/// Indicates the return value of an instruction that was not evaluated during
95-
/// interpretation.
96-
ReturnedByUnevaluatedInstruction,
117+
public:
118+
UnknownKind getKind() { return kind; }
97119

98-
/// Indicates that the value was possibly modified by an instruction
99-
/// that was not evaluated during the interpretation.
100-
MutatedByUnevaluatedInstruction,
120+
static bool isUnknownKindWithPayload(UnknownKind kind) {
121+
return kind == UnknownKind::CalleeImplementationUnknown;
122+
}
123+
124+
static UnknownReason create(UnknownKind kind) {
125+
assert(!isUnknownKindWithPayload(kind));
126+
UnknownReason reason;
127+
reason.kind = kind;
128+
return reason;
129+
}
130+
131+
static UnknownReason createCalleeImplementationUnknown(SILFunction *callee) {
132+
assert(callee);
133+
UnknownReason reason;
134+
reason.kind = UnknownKind::CalleeImplementationUnknown;
135+
reason.payload.function = callee;
136+
return reason;
137+
}
138+
139+
SILFunction *getCalleeWithoutImplmentation() {
140+
assert(kind == UnknownKind::CalleeImplementationUnknown);
141+
return payload.function;
142+
}
143+
144+
static UnknownReason createAssertionFailure(const char *message,
145+
size_t size) {
146+
assert(message[size] == '\0' && "message must be null-terminated");
147+
UnknownReason reason;
148+
reason.kind = UnknownKind::AssertionFailure;
149+
reason.payload.failedAssertMessage = message;
150+
return reason;
151+
}
152+
153+
const char *getAssertionFailureMessage() {
154+
assert(kind == UnknownKind::AssertionFailure);
155+
return payload.failedAssertMessage;
156+
}
101157
};
102158

103159
/// An abstract class that exposes functions for allocating symbolic values.
@@ -241,9 +297,6 @@ class SymbolicValue {
241297
RepresentationKind representationKind : 8;
242298

243299
union {
244-
/// This is the reason code for RK_Unknown values.
245-
UnknownReason unknownReason : 32;
246-
247300
/// This is the number of bits in an RK_Integer or RK_IntegerInline
248301
/// representation, which makes the number of entries in the list derivable.
249302
unsigned integerBitwidth;

include/swift/SILOptimizer/Utils/ConstExpr.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class SILNode;
3838
class SymbolicValue;
3939
class SymbolicValueAllocator;
4040
class ConstExprFunctionState;
41-
enum class UnknownReason;
41+
class UnknownReason;
4242

4343
/// This class is the main entrypoint for evaluating constant expressions. It
4444
/// also handles caching of previously computed constexpr results.
@@ -162,8 +162,6 @@ class ConstExprStepEvaluator {
162162

163163
Optional<SymbolicValue> lookupConstValue(SILValue value);
164164

165-
bool isKnownFunction(SILFunction *fun);
166-
167165
/// Returns true if and only if `errorVal` denotes an error that requires
168166
/// aborting interpretation and returning the error. Skipping an instruction
169167
/// that produces such errors is not a valid behavior.
@@ -177,5 +175,7 @@ class ConstExprStepEvaluator {
177175
unsigned instructionsEvaluatedByLastEvaluation() { return stepsEvaluated; }
178176
};
179177

178+
bool isKnownConstantEvaluableFunction(SILFunction *fun);
179+
180180
} // end namespace swift
181181
#endif

lib/AST/Builtins.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1779,7 +1779,7 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
17791779
#define BUILTIN_BINARY_OPERATION(id, name, attrs, overload) case BuiltinValueKind::id:
17801780
#include "swift/AST/Builtins.def"
17811781
if (Types.size() != 1) return nullptr;
1782-
return getBinaryOperation(Id, Types[0]);
1782+
return getBinaryOperation(Id, Types[0]);
17831783

17841784
#define BUILTIN(id, name, Attrs)
17851785
#define BUILTIN_BINARY_OPERATION_WITH_OVERFLOW(id, name, _, attrs, overload) case BuiltinValueKind::id:

lib/SIL/OperandOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,7 @@ ANY_OWNERSHIP_BUILTIN(SRem)
10031003
ANY_OWNERSHIP_BUILTIN(SSubOver)
10041004
ANY_OWNERSHIP_BUILTIN(SToSCheckedTrunc)
10051005
ANY_OWNERSHIP_BUILTIN(SToUCheckedTrunc)
1006+
ANY_OWNERSHIP_BUILTIN(Expect)
10061007
ANY_OWNERSHIP_BUILTIN(Shl)
10071008
ANY_OWNERSHIP_BUILTIN(Sizeof)
10081009
ANY_OWNERSHIP_BUILTIN(StaticReport)

0 commit comments

Comments
 (0)