blob: cb6cac6b97a6e0542b4735ea2493834d9b994700 [file] [log] [blame]
//===--- ConstExpr.h - Constant expression evaluator -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This defines an interface to evaluate Swift language level constant
// expressions. Its model is intended to be general and reasonably powerful,
// with the goal of standardization in a future version of Swift.
//
// Constant expressions are functions without side effects that take constant
// values and return constant values. These constants may be integer, and
// floating point values. We allow abstractions to be built out of fragile
// structs and tuples.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SILOPTIMIZER_CONSTEXPR_H
#define SWIFT_SILOPTIMIZER_CONSTEXPR_H
#include "swift/Basic/LLVM.h"
#include "swift/Basic/SourceLoc.h"
#include "swift/SIL/SILBasicBlock.h"
#include "llvm/ADT/SmallPtrSet.h"
namespace swift {
class ASTContext;
class Operand;
class SILFunction;
class SILModule;
class SILNode;
class SymbolicValue;
class SymbolicValueAllocator;
class ConstExprFunctionState;
class UnknownReason;
/// This class is the main entrypoint for evaluating constant expressions. It
/// also handles caching of previously computed constexpr results.
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;
/// When set to true, keep track of all functions called during an evaluation.
bool trackCallees;
/// Functions called during the evaluation. This is an auxiliary information
/// provided to the clients.
llvm::SmallPtrSet<SILFunction *, 2> calledFunctions;
void operator=(const ConstExprEvaluator &) = delete;
public:
explicit ConstExprEvaluator(SymbolicValueAllocator &alloc,
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() {
assert(!callStack.empty());
callStack.pop_back();
}
const llvm::SmallVector<SourceLoc, 4> &getCallStack() { return callStack; }
// As SymbolicValue::getUnknown(), but handles passing the call stack and
// allocator.
SymbolicValue getUnknown(SILNode *node, UnknownReason reason);
/// Analyze the specified values to determine if they are constant values.
/// This is done in code that is not necessarily itself a constexpr
/// function. The results are added to the results list which is a parallel
/// structure to the input values.
void computeConstantValues(ArrayRef<SILValue> values,
SmallVectorImpl<SymbolicValue> &results);
void recordCalledFunctionIfEnabled(SILFunction *callee) {
if (trackCallees) {
calledFunctions.insert(callee);
}
}
/// If the evaluator was initialized with \c trackCallees enabled, return the
/// SIL functions encountered during the evaluations performed with this
/// evaluator. The returned functions include those that were called but
/// failed to complete successfully.
const SmallPtrSetImpl<SILFunction *> &getFuncsCalledDuringEvaluation() const {
assert(trackCallees && "evaluator not configured to track callees");
return calledFunctions;
}
};
/// A constant-expression evaluator that can be used to step through a control
/// flow graph (SILFunction body) by evaluating one instruction at a time.
/// This evaluator can also "skip" instructions without evaluating them and
/// only track constant values of variables whose values could be computed.
class ConstExprStepEvaluator {
private:
ConstExprEvaluator evaluator;
ConstExprFunctionState *internalState;
unsigned stepsEvaluated = 0;
/// Targets of branches that were visited. This is used to detect loops during
/// evaluation.
SmallPtrSet<SILBasicBlock *, 8> visitedBlocks;
ConstExprStepEvaluator(const ConstExprStepEvaluator &) = delete;
void operator=(const ConstExprStepEvaluator &) = delete;
public:
/// Constructs a step evaluator given an allocator and a non-null pointer to a
/// SILFunction.
explicit ConstExprStepEvaluator(SymbolicValueAllocator &alloc,
SILFunction *fun, unsigned assertConf,
bool trackCallees = false);
~ConstExprStepEvaluator();
/// Evaluate an instruction in the current interpreter state.
/// \param instI instruction to be evaluated in the current interpreter state.
/// \returns a pair where the first and second elements are defined as
/// follows:
/// The first element is the iterator to the next instruction from where
/// the evaluation can continue, if the evaluation is successful.
/// Otherwise, it is None.
///
/// Second element is None, if the evaluation is successful.
/// Otherwise, is an unknown symbolic value that contains the error.
std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>>
evaluate(SILBasicBlock::iterator instI);
/// Skip the instruction without evaluating it and conservatively account for
/// the effects of the instruction on the internal state. This operation
/// resets to an unknown symbolic value any portion of a
/// SymbolicValueMemoryObject that could possibly be mutated by the given
/// instruction. This function preserves the soundness of the interpretation.
/// \param instI instruction to be skipped.
/// \returns a pair where the first and second elements are defined as
/// follows:
/// The first element, if is not None, is the iterator to the next
/// instruction from the where the evaluation must continue.
/// The first element is None if the next instruction from where the
/// evaluation must continue cannot be determined.
/// This would be the case if `instI` is a branch like a `condbr`.
///
/// Second element is None if skipping the instruction is successful.
/// Otherwise, it is an unknown symbolic value containing the error.
std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>>
skipByMakingEffectsNonConstant(SILBasicBlock::iterator instI);
/// Try evaluating an instruction and if the evaluation fails, skip the
/// instruction and make it effects non constant. Note that it may not always
/// be possible to skip an instruction whose evaluation failed and
/// continue evalution (e.g. a conditional branch).
/// See `evaluate` and `skipByMakingEffectsNonConstant` functions for their
/// semantics.
/// \param instI instruction to be evaluated in the current interpreter state.
/// \returns a pair where the first and second elements are defined as
/// follows:
/// The first element, if is not None, is the iterator to the next
/// instruction from the where the evaluation must continue.
/// The first element is None iff both `evaluate` and `skip` functions
/// failed to determine the next instruction to continue evaluation from.
///
/// Second element is None if the evaluation is successful.
/// Otherwise, it is an unknown symbolic value containing the error.
std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>>
tryEvaluateOrElseMakeEffectsNonConstant(SILBasicBlock::iterator instI);
Optional<SymbolicValue> lookupConstValue(SILValue value);
/// Returns true if and only if `errorVal` denotes an error that requires
/// aborting interpretation and returning the error. Skipping an instruction
/// that produces such errors is not a valid behavior.
bool isFailStopError(SymbolicValue errorVal);
/// Return the number of instructions evaluated for the last `evaluate`
/// operation. This could be used by the clients to limit the number of
/// instructions that should be evaluated by the step-wise evaluator.
/// Note that 'skipByMakingEffectsNonConstant' operation is not considered
/// as an evaluation.
unsigned instructionsEvaluatedByLastEvaluation() { return stepsEvaluated; }
/// If the evaluator was initialized with \c trackCallees enabled, return the
/// SIL functions encountered during the evaluations performed with this
/// evaluator. The returned functions include those that were called but
/// failed to complete successfully. Targets of skipped apply instructions
/// will not be included in the returned set.
const SmallPtrSetImpl<SILFunction *> &getFuncsCalledDuringEvaluation() {
return evaluator.getFuncsCalledDuringEvaluation();
}
};
bool isKnownConstantEvaluableFunction(SILFunction *fun);
} // end namespace swift
#endif