Skip to content

[Constant Evaluator] Improve assertion failure and condfail_message support in constant evaluator #27244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -377,15 +377,10 @@ NOTE(constexpr_overflow,none, "integer overflow detected", ())
NOTE(constexpr_overflow_operation,none, "operation"
"%select{| performed during this call}0 overflows", (bool))

NOTE(constexpr_trap,none, "trap detected", ())
NOTE(constexpr_trap_operation,none, "operation"
NOTE(constexpr_trap, none, "%0", (StringRef))
NOTE(constexpr_trap_operation, none, "operation"
"%select{| performed during this call}0 traps", (bool))

NOTE(constexpr_assertion_failed, none, "assertion failed with message: %0",
(StringRef))
NOTE(constexpr_assertion_failed_here, none, "assertion failed"
"%select{ here| during this call}0 ", (bool))

NOTE(constexpr_invalid_operand_seen, none,
"operation with invalid operands encountered during evaluation",())
NOTE(constexpr_operand_invalid_here, none,
Expand Down
115 changes: 61 additions & 54 deletions include/swift/SIL/SILConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,45 @@ struct UnknownSymbolicValue;

extern llvm::cl::opt<unsigned> ConstExprLimit;

/// An abstract class that exposes functions for allocating symbolic values.
/// The implementors of this class have to determine where to allocate them and
/// and manage the lifetime of the allocated symbolic values.
class SymbolicValueAllocator {
public:
virtual ~SymbolicValueAllocator() {}

/// Allocate raw bytes.
/// \param byteSize number of bytes to allocate.
/// \param alignment alignment for the allocated bytes.
virtual void *allocate(unsigned long byteSize, unsigned alignment) = 0;

/// Allocate storage for a given number of elements of a specific type
/// provided as a template parameter. Precondition: \c T must have an
/// accesible zero argument constructor.
/// \param numElts number of elements of the type to allocate.
template <typename T> T *allocate(unsigned numElts) {
T *res = (T *)allocate(sizeof(T) * numElts, alignof(T));
for (unsigned i = 0; i != numElts; ++i)
new (res + i) T();
return res;
}
};

/// A class that allocates symbolic values in a local bump allocator. The
/// lifetime of the bump allocator is same as the lifetime of \c this object.
class SymbolicValueBumpAllocator : public SymbolicValueAllocator {
private:
llvm::BumpPtrAllocator bumpAllocator;

public:
SymbolicValueBumpAllocator() {}
~SymbolicValueBumpAllocator() {}

void *allocate(unsigned long byteSize, unsigned alignment) {
return bumpAllocator.Allocate(byteSize, alignment);
}
};

/// When we fail to constant fold a value, this captures a reason why,
/// allowing the caller to produce a specific diagnostic. The "Unknown"
/// SymbolicValue representation also includes a pointer to the SILNode in
Expand All @@ -58,13 +97,9 @@ class UnknownReason {
/// Integer overflow detected.
Overflow,

/// Unspecified trap detected.
/// Trap detected. Traps will a message as a payload.
Trap,

/// Assertion failure detected. These have an associated message unlike
/// traps.
AssertionFailure,

/// An operation was applied over operands whose symbolic values were
/// constants but were not valid for the operation.
InvalidOperandValue,
Expand Down Expand Up @@ -111,14 +146,20 @@ class UnknownReason {
// Auxiliary information for different unknown kinds.
union {
SILFunction *function;
const char *failedAssertMessage;
const char *trapMessage;
} payload;

public:
UnknownKind getKind() { return kind; }

static bool isUnknownKindWithPayload(UnknownKind kind) {
return kind == UnknownKind::CalleeImplementationUnknown;
switch (kind) {
case UnknownKind::CalleeImplementationUnknown:
case UnknownKind::Trap:
return true;
default:
return false;
}
}

static UnknownReason create(UnknownKind kind) {
Expand All @@ -141,57 +182,23 @@ class UnknownReason {
return payload.function;
}

static UnknownReason createAssertionFailure(const char *message,
size_t size) {
assert(message[size] == '\0' && "message must be null-terminated");
static UnknownReason createTrap(StringRef message,
SymbolicValueAllocator &allocator) {
// Copy and null terminate the string.
size_t size = message.size();
char *messagePtr = allocator.allocate<char>(size + 1);
std::uninitialized_copy(message.begin(), message.end(), messagePtr);
messagePtr[size] = '\0';

UnknownReason reason;
reason.kind = UnknownKind::AssertionFailure;
reason.payload.failedAssertMessage = message;
reason.kind = UnknownKind::Trap;
reason.payload.trapMessage = messagePtr;
return reason;
}

const char *getAssertionFailureMessage() {
assert(kind == UnknownKind::AssertionFailure);
return payload.failedAssertMessage;
}
};

/// An abstract class that exposes functions for allocating symbolic values.
/// The implementors of this class have to determine where to allocate them and
/// and manage the lifetime of the allocated symbolic values.
class SymbolicValueAllocator {
public:
virtual ~SymbolicValueAllocator() {}

/// Allocate raw bytes.
/// \param byteSize number of bytes to allocate.
/// \param alignment alignment for the allocated bytes.
virtual void *allocate(unsigned long byteSize, unsigned alignment) = 0;

/// Allocate storage for a given number of elements of a specific type
/// provided as a template parameter. Precondition: \c T must have an
/// accesible zero argument constructor.
/// \param numElts number of elements of the type to allocate.
template <typename T> T *allocate(unsigned numElts) {
T *res = (T *)allocate(sizeof(T) * numElts, alignof(T));
for (unsigned i = 0; i != numElts; ++i)
new (res + i) T();
return res;
}
};

/// A class that allocates symbolic values in a local bump allocator. The
/// lifetime of the bump allocator is same as the lifetime of \c this object.
class SymbolicValueBumpAllocator : public SymbolicValueAllocator {
private:
llvm::BumpPtrAllocator bumpAllocator;

public:
SymbolicValueBumpAllocator() {}
~SymbolicValueBumpAllocator() {}

void *allocate(unsigned long byteSize, unsigned alignment) {
return bumpAllocator.Allocate(byteSize, alignment);
const char *getTrapMessage() {
assert(kind == UnknownKind::Trap);
return payload.trapMessage;
}
};

Expand Down
11 changes: 9 additions & 2 deletions include/swift/SILOptimizer/Utils/ConstExpr.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class UnknownReason;
class ConstExprEvaluator {
SymbolicValueAllocator &allocator;

// Assert configuration that must be used by the evaluator. This determines
// the result of the builtin "assert_configuration".
unsigned assertConfig;

/// The current call stack, used for providing accurate diagnostics.
llvm::SmallVector<SourceLoc, 4> callStack;

Expand All @@ -58,13 +62,15 @@ class ConstExprEvaluator {

public:
explicit ConstExprEvaluator(SymbolicValueAllocator &alloc,
bool trackCallees = false);
unsigned assertConf, bool trackCallees = false);
~ConstExprEvaluator();

explicit ConstExprEvaluator(const ConstExprEvaluator &other);

SymbolicValueAllocator &getAllocator() { return allocator; }

unsigned getAssertConfig() { return assertConfig; }

void pushCallStack(SourceLoc loc) { callStack.push_back(loc); }

void popCallStack() {
Expand Down Expand Up @@ -124,7 +130,8 @@ class ConstExprStepEvaluator {
/// Constructs a step evaluator given an allocator and a non-null pointer to a
/// SILFunction.
explicit ConstExprStepEvaluator(SymbolicValueAllocator &alloc,
SILFunction *fun, bool trackCallees = false);
SILFunction *fun, unsigned assertConf,
bool trackCallees = false);
~ConstExprStepEvaluator();

/// Evaluate an instruction in the current interpreter state.
Expand Down
13 changes: 3 additions & 10 deletions lib/SIL/SILConstants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -656,20 +656,13 @@ void SymbolicValue::emitUnknownDiagnosticNotes(SILLocation fallbackLoc) {
diagnose(ctx, triggerLoc, diag::constexpr_overflow_operation,
triggerLocSkipsInternalLocs);
return;
case UnknownReason::Trap:
diagnose(ctx, diagLoc, diag::constexpr_trap);
case UnknownReason::Trap: {
const char *message = unknownReason.getTrapMessage();
diagnose(ctx, diagLoc, diag::constexpr_trap, StringRef(message));
if (emitTriggerLocInDiag)
diagnose(ctx, triggerLoc, diag::constexpr_trap_operation,
triggerLocSkipsInternalLocs);
return;
case UnknownReason::AssertionFailure: {
const char *message = unknownReason.getAssertionFailureMessage();
diagnose(ctx, diagLoc, diag::constexpr_assertion_failed,
StringRef(message));
if (emitTriggerLocInDiag)
diagnose(ctx, triggerLoc, diag::constexpr_assertion_failed_here,
triggerLocSkipsInternalLocs);
return;
}
case UnknownReason::InvalidOperandValue:
diagnose(ctx, diagLoc, diag::constexpr_invalid_operand_seen);
Expand Down
3 changes: 2 additions & 1 deletion lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ class EmitDFDiagnostics : public SILFunctionTransform {

if (M.getASTContext().LangOpts.EnableExperimentalStaticAssert) {
SymbolicValueBumpAllocator allocator;
ConstExprEvaluator constantEvaluator(allocator);
ConstExprEvaluator constantEvaluator(allocator,
getOptions().AssertConfig);
for (auto &BB : *getFunction())
for (auto &I : BB)
diagnosePoundAssert(&I, M, constantEvaluator);
Expand Down
13 changes: 8 additions & 5 deletions lib/SILOptimizer/Mandatory/OSLogOptimization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,10 @@ class FoldState {
SmallVector<SILValue, 4> constantSILValues;

public:
FoldState(SILFunction *fun, SILInstruction *beginInst,
FoldState(SILFunction *fun, unsigned assertConfig, SILInstruction *beginInst,
ArrayRef<SILInstruction *> endInsts)
: constantEvaluator(allocator, fun), beginInstruction(beginInst),
: constantEvaluator(allocator, fun, assertConfig),
beginInstruction(beginInst),
endInstructions(endInsts.begin(), endInsts.end()) {}

void addConstantSILValue(SILValue value) {
Expand Down Expand Up @@ -627,13 +628,14 @@ static bool detectAndDiagnoseErrors(Optional<SymbolicValue> errorInfo,
/// Constant evaluate instructions starting from 'start' and fold the uses
/// of the value 'oslogMessage'. Stop when oslogMessageValue is released.
static void constantFold(SILInstruction *start,
SingleValueInstruction *oslogMessage) {
SingleValueInstruction *oslogMessage,
unsigned assertConfig) {

// Initialize fold state.
SmallVector<SILInstruction *, 2> lifetimeEndInsts;
getLifetimeEndInstructionsOfSILValue(oslogMessage, lifetimeEndInsts);

FoldState state(start->getFunction(), start, lifetimeEndInsts);
FoldState state(start->getFunction(), assertConfig, start, lifetimeEndInsts);

auto errorInfo = collectConstants(state);

Expand Down Expand Up @@ -738,6 +740,7 @@ class OSLogOptimization : public SILFunctionTransform {
/// The entry point to the transformation.
void run() override {
auto &fun = *getFunction();
unsigned assertConfig = getOptions().AssertConfig;

// Don't rerun optimization on deserialized functions or stdlib functions.
if (fun.wasDeserializedCanonical()) {
Expand Down Expand Up @@ -778,7 +781,7 @@ class OSLogOptimization : public SILFunctionTransform {
continue;
}

constantFold(interpolationStart, oslogInit);
constantFold(interpolationStart, oslogInit, assertConfig);
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class ConstantEvaluableSubsetChecker : public SILModuleTransform {
// Create a step evaluator and run it on the function.
SymbolicValueBumpAllocator allocator;
ConstExprStepEvaluator stepEvaluator(allocator, fun,
getOptions().AssertConfig,
/*trackCallees*/ true);
bool previousEvaluationHadFatalError = false;

Expand Down
3 changes: 2 additions & 1 deletion lib/SILOptimizer/UtilityPasses/ConstantEvaluatorTester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class ConstantEvaluatorTester : public SILFunctionTransform {
llvm::errs() << "@" << fun->getName() << "\n";

SymbolicValueBumpAllocator allocator;
ConstExprStepEvaluator stepEvaluator(allocator, fun);
ConstExprStepEvaluator stepEvaluator(allocator, fun,
getOptions().AssertConfig);

for (auto currI = fun->getEntryBlock()->begin();;) {
auto *inst = &(*currI);
Expand Down
Loading