| //===--- ConstExpr.cpp - Constant expression evaluator -------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #define DEBUG_TYPE "ConstExpr" |
| #include "swift/SILOptimizer/Utils/ConstExpr.h" |
| #include "swift/AST/ProtocolConformance.h" |
| #include "swift/AST/SubstitutionMap.h" |
| #include "swift/Basic/Defer.h" |
| #include "swift/Basic/NullablePtr.h" |
| #include "swift/Demangling/Demangle.h" |
| #include "swift/SIL/ApplySite.h" |
| #include "swift/SIL/FormalLinkage.h" |
| #include "swift/SIL/SILBuilder.h" |
| #include "swift/SIL/SILConstants.h" |
| #include "swift/SILOptimizer/Utils/Devirtualize.h" |
| #include "swift/Serialization/SerializedSILLoader.h" |
| #include "llvm/ADT/PointerEmbeddedInt.h" |
| #include "llvm/Support/TrailingObjects.h" |
| |
| using namespace swift; |
| |
| static llvm::Optional<SymbolicValue> |
| evaluateAndCacheCall(SILFunction &fn, SubstitutionMap substitutionMap, |
| ArrayRef<SymbolicValue> arguments, SymbolicValue &result, |
| unsigned &numInstEvaluated, ConstExprEvaluator &evaluator); |
| |
| // TODO: ConstantTracker in the performance inliner and the |
| // ConstantFolding.h/cpp files should be subsumed by this, as this is a more |
| // general framework. |
| |
| enum class WellKnownFunction { |
| // Array.init() |
| ArrayInitEmpty, |
| // Array._allocateUninitializedArray |
| AllocateUninitializedArray, |
| // Array.append(_:) |
| ArrayAppendElement, |
| // String.init() |
| StringInitEmpty, |
| // String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) |
| StringMakeUTF8, |
| // static String.append (_: String, _: inout String) |
| StringAppend, |
| // static String.== infix(_: String) |
| StringEquals, |
| // String.percentEscapedString.getter |
| StringEscapePercent, |
| // _assertionFailure(_: StaticString, _: StaticString, file: StaticString,...) |
| AssertionFailure |
| }; |
| |
| static llvm::Optional<WellKnownFunction> classifyFunction(SILFunction *fn) { |
| if (fn->hasSemanticsAttr("array.init.empty")) |
| return WellKnownFunction::ArrayInitEmpty; |
| if (fn->hasSemanticsAttr("array.uninitialized_intrinsic")) |
| return WellKnownFunction::AllocateUninitializedArray; |
| if (fn->hasSemanticsAttr("array.append_element")) |
| return WellKnownFunction::ArrayAppendElement; |
| if (fn->hasSemanticsAttr("string.init_empty")) |
| return WellKnownFunction::StringInitEmpty; |
| // There are two string initializers in the standard library with the |
| // semantics "string.makeUTF8". They are identical from the perspective of |
| // the interpreter. One of those functions is probably redundant and not used. |
| if (fn->hasSemanticsAttr("string.makeUTF8")) |
| return WellKnownFunction::StringMakeUTF8; |
| if (fn->hasSemanticsAttr("string.append")) |
| return WellKnownFunction::StringAppend; |
| if (fn->hasSemanticsAttr("string.equals")) |
| return WellKnownFunction::StringEquals; |
| if (fn->hasSemanticsAttr("string.escapePercent.get")) |
| return WellKnownFunction::StringEscapePercent; |
| if (fn->hasSemanticsAttrThatStartsWith("programtermination_point")) |
| return WellKnownFunction::AssertionFailure; |
| return None; |
| } |
| |
| /// Helper function for creating UnknownReason without a payload. |
| static SymbolicValue getUnknown(ConstExprEvaluator &evaluator, SILNode *node, |
| UnknownReason::UnknownKind kind) { |
| return evaluator.getUnknown(node, UnknownReason::create(kind)); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // ConstExprFunctionState implementation. |
| //===----------------------------------------------------------------------===// |
| |
| namespace swift { |
| /// This type represents the state of computed values within a function |
| /// as evaluation happens. A separate instance of this is made for each |
| /// callee in a call chain to represent the constant values given the set of |
| /// formal parameters that callee was invoked with. |
| class ConstExprFunctionState { |
| /// This is the evaluator that is computing this function state. We use it to |
| /// allocate space for values and to query the call stack. |
| ConstExprEvaluator &evaluator; |
| |
| /// If we are analyzing the body of a constexpr function, this is the |
| /// function. This is null for the top-level expression. |
| SILFunction *fn; |
| |
| /// If we have a function being analyzed, this is the substitutionMap for |
| /// the call to it. |
| /// substitutionMap specifies a mapping from all of the protocol and type |
| /// requirements in the generic signature down to concrete conformances and |
| /// concrete types. |
| SubstitutionMap substitutionMap; |
| |
| /// This keeps track of the number of instructions we've evaluated. If this |
| /// goes beyond the execution cap, then we start returning unknown values. |
| unsigned &numInstEvaluated; |
| |
| /// This is a state of previously analyzed values, maintained and filled in |
| /// by getConstantValue. This does not hold the memory referred to by SIL |
| /// addresses. |
| llvm::DenseMap<SILValue, SymbolicValue> calculatedValues; |
| |
| /// If a SILValue is not bound to a SymbolicValue in the calculatedValues, |
| /// try to compute it recursively by visiting its defining instruction. |
| bool recursivelyComputeValueIfNotInState = false; |
| |
| public: |
| ConstExprFunctionState(ConstExprEvaluator &evaluator, SILFunction *fn, |
| SubstitutionMap substitutionMap, |
| unsigned &numInstEvaluated, |
| bool enableTopLevelEvaluation) |
| : evaluator(evaluator), fn(fn), substitutionMap(substitutionMap), |
| numInstEvaluated(numInstEvaluated), |
| recursivelyComputeValueIfNotInState(enableTopLevelEvaluation) { |
| assert((!fn || !enableTopLevelEvaluation) && |
| "top-level evaluation must be disabled when evaluating a function" |
| " body step by step"); |
| } |
| |
| /// Pretty print the state to stderr. |
| void dump() const { |
| llvm::errs() << "[ConstExprState: \n"; |
| llvm::errs() << " Caller: " << (fn ? fn->getName() : "null") << "\n"; |
| llvm::errs() << " evaluatedInstrCount: " << numInstEvaluated << "\n"; |
| llvm::errs() << " SubstMap: \n"; |
| substitutionMap.dump(llvm::errs(), SubstitutionMap::DumpStyle::Full, 6); |
| llvm::errs() << "\n calculatedValues: "; |
| for (auto kv : calculatedValues) { |
| llvm::errs() << " " << kv.first << " --> " << kv.second << "\n"; |
| } |
| } |
| |
| void setValue(SILValue value, SymbolicValue symVal) { |
| calculatedValues.insert({value, symVal}); |
| } |
| |
| /// Return the symbolic value for a SILValue if it is bound in the interpreter |
| /// state. If not, return None. |
| llvm::Optional<SymbolicValue> lookupValue(SILValue value) { |
| auto it = calculatedValues.find(value); |
| if (it != calculatedValues.end()) |
| return it->second; |
| return None; |
| } |
| |
| /// Invariant: Before the call, `calculatedValues` must not contain `addr` |
| /// as a key. |
| SymbolicValue createMemoryObject(SILValue addr, SymbolicValue initialValue) { |
| assert(!calculatedValues.count(addr)); |
| auto type = substituteGenericParamsAndSimpify(addr->getType().getASTType()); |
| auto *memObject = SymbolicValueMemoryObject::create( |
| type, initialValue, evaluator.getAllocator()); |
| auto result = SymbolicValue::getAddress(memObject); |
| setValue(addr, result); |
| return result; |
| } |
| |
| /// Return the SymbolicValue for the specified SIL value. If the SIL value is |
| /// not in \c calculatedValues, try computing the SymbolicValue recursively |
| /// if \c recursivelyComputeValueIfNotInState flag is set. |
| SymbolicValue getConstantValue(SILValue value); |
| |
| /// Evaluate the specified instruction in a flow sensitive way, for use by |
| /// the constexpr function evaluator. This does not handle control flow |
| /// statements. |
| llvm::Optional<SymbolicValue> evaluateFlowSensitive(SILInstruction *inst); |
| |
| /// Evaluate a branch or non-branch instruction and if the evaluation was |
| /// successful, return the next instruction from where the evaluation must |
| /// continue. |
| /// \param instI basic-block iterator pointing to the instruction to evaluate. |
| /// \param visitedBlocks basic blocks already visited during evaluation. |
| /// This is used to detect loops. |
| /// \returns a pair where the first and second elements are defined as |
| /// follows: |
| /// If the evaluation of the instruction is successful, the first element |
| /// is the iterator to the next instruction from the where the evaluation |
| /// must continue. 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>> |
| evaluateInstructionAndGetNext( |
| SILBasicBlock::iterator instI, |
| SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks); |
| |
| Type substituteGenericParamsAndSimpify(Type ty); |
| CanType substituteGenericParamsAndSimpify(CanType ty) { |
| return substituteGenericParamsAndSimpify(Type(ty))->getCanonicalType(); |
| } |
| SymbolicValue computeConstantValue(SILValue value); |
| SymbolicValue computeConstantValueBuiltin(BuiltinInst *inst); |
| |
| llvm::Optional<SymbolicValue> computeCallResult(ApplyInst *apply); |
| |
| llvm::Optional<SymbolicValue> computeOpaqueCallResult(ApplyInst *apply, |
| SILFunction *callee); |
| |
| llvm::Optional<SymbolicValue> |
| computeWellKnownCallResult(ApplyInst *apply, WellKnownFunction callee); |
| |
| SymbolicValue getSingleWriterAddressValue(SILValue addr); |
| SymbolicValue getConstAddrAndLoadResult(SILValue addr); |
| SymbolicValue loadAddrValue(SILValue addr, SymbolicValue addrVal); |
| llvm::Optional<SymbolicValue> computeFSStore(SymbolicValue storedCst, |
| SILValue dest); |
| private: |
| llvm::Optional<SymbolicValue> |
| initializeAddressFromSingleWriter(SILValue addr); |
| }; |
| } // namespace swift |
| |
| /// Simplify the specified type based on knowledge of substitutions if we have |
| /// any. |
| Type ConstExprFunctionState::substituteGenericParamsAndSimpify(Type ty) { |
| return substitutionMap.empty() ? ty : ty.subst(substitutionMap); |
| } |
| |
| /// Const-evaluate `value`, which must not have been computed. |
| SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { |
| assert(!calculatedValues.count(value)); |
| |
| // If this a trivial constant instruction that we can handle, then fold it |
| // immediately. |
| if (auto *ili = dyn_cast<IntegerLiteralInst>(value)) |
| return SymbolicValue::getInteger(ili->getValue(), evaluator.getAllocator()); |
| if (auto *sli = dyn_cast<StringLiteralInst>(value)) |
| return SymbolicValue::getString(sli->getValue(), evaluator.getAllocator()); |
| |
| if (auto *fri = dyn_cast<FunctionRefInst>(value)) |
| return SymbolicValue::getFunction(fri->getInitiallyReferencedFunction()); |
| |
| // If we have a reference to a metatype, constant fold any substitutable |
| // types. |
| if (auto *mti = dyn_cast<MetatypeInst>(value)) { |
| auto metatype = mti->getType().castTo<MetatypeType>(); |
| auto type = substituteGenericParamsAndSimpify(metatype->getInstanceType()) |
| ->getCanonicalType(); |
| return SymbolicValue::getMetatype(type); |
| } |
| |
| if (auto *tei = dyn_cast<TupleExtractInst>(value)) { |
| auto val = getConstantValue(tei->getOperand()); |
| if (!val.isConstant()) |
| return val; |
| return val.getAggregateValue()[tei->getFieldNo()]; |
| } |
| |
| // If this is a struct extract from a fragile type, then we can return the |
| // element being extracted. |
| if (auto *sei = dyn_cast<StructExtractInst>(value)) { |
| auto aggValue = sei->getOperand(); |
| auto val = getConstantValue(aggValue); |
| if (!val.isConstant()) { |
| return val; |
| } |
| assert(val.getKind() == SymbolicValue::Aggregate); |
| return val.getAggregateValue()[sei->getFieldNo()]; |
| } |
| |
| // If this is an unchecked_enum_data from a fragile type, then we can return |
| // the enum case value. |
| if (auto *uedi = dyn_cast<UncheckedEnumDataInst>(value)) { |
| auto aggValue = uedi->getOperand(); |
| auto val = getConstantValue(aggValue); |
| if (val.isConstant()) { |
| assert(val.getKind() == SymbolicValue::EnumWithPayload); |
| return val.getEnumPayloadValue(); |
| } |
| // Not a const. |
| return val; |
| } |
| |
| // If this is a destructure_result, then we can return the element being |
| // extracted. |
| if (isa<DestructureStructResult>(value) || |
| isa<DestructureTupleResult>(value)) { |
| auto *result = cast<MultipleValueInstructionResult>(value); |
| SILValue aggValue = result->getParent()->getOperand(0); |
| auto val = getConstantValue(aggValue); |
| if (val.isConstant()) { |
| assert(val.getKind() == SymbolicValue::Aggregate); |
| return val.getAggregateValue()[result->getIndex()]; |
| } |
| // Not a const. |
| return val; |
| } |
| |
| // TODO: If this is a single element struct, we can avoid creating an |
| // aggregate to reduce # allocations. This is extra silly in the case of zero |
| // element tuples. |
| if (isa<StructInst>(value) || isa<TupleInst>(value)) { |
| auto *inst = cast<SingleValueInstruction>(value); |
| SmallVector<SymbolicValue, 4> elts; |
| |
| for (unsigned i = 0, e = inst->getNumOperands(); i != e; ++i) { |
| auto val = getConstantValue(inst->getOperand(i)); |
| if (!val.isConstant() && !val.isUnknownDueToUnevaluatedInstructions()) |
| return val; |
| // Unknown values due to unevaluated instructions can be assigned to |
| // struct properties as they are not indicative of a fatal error or |
| // trap. |
| elts.push_back(val); |
| } |
| |
| return SymbolicValue::getAggregate(elts, evaluator.getAllocator()); |
| } |
| |
| // If this is a struct or tuple element addressor, compute a more derived |
| // address. |
| if (isa<StructElementAddrInst>(value) || isa<TupleElementAddrInst>(value)) { |
| auto inst = cast<SingleValueInstruction>(value); |
| auto baseAddr = getConstantValue(inst->getOperand(0)); |
| if (!baseAddr.isConstant()) |
| return baseAddr; |
| |
| SmallVector<unsigned, 4> accessPath; |
| auto *memObject = baseAddr.getAddressValue(accessPath); |
| |
| // Add our index onto the next of the list. |
| unsigned index; |
| if (auto sea = dyn_cast<StructElementAddrInst>(inst)) |
| index = sea->getFieldNo(); |
| else |
| index = cast<TupleElementAddrInst>(inst)->getFieldNo(); |
| accessPath.push_back(index); |
| return SymbolicValue::getAddress(memObject, accessPath, |
| evaluator.getAllocator()); |
| } |
| |
| // If this is a load, then we either have computed the value of the memory |
| // already (when analyzing the body of a function in a flow-sensitive |
| // fashion), or this is the indirect result of a call. Either way, we ask for |
| // the value of the pointer. In the former case, this will be the latest |
| // value of the memory. In the latter case, the call must be the only |
| // store to the address so that the memory object can be computed by |
| // recursively processing the allocation and call instructions in a |
| // demand-driven fashion. |
| if (auto li = dyn_cast<LoadInst>(value)) |
| return getConstAddrAndLoadResult(li->getOperand()); |
| |
| // Try to resolve a witness method against our known conformances. |
| if (auto *wmi = dyn_cast<WitnessMethodInst>(value)) { |
| auto confResult = substitutionMap.lookupConformance( |
| wmi->getLookupType(), wmi->getConformance().getRequirement()); |
| if (!confResult) |
| return getUnknown(evaluator, value, |
| UnknownReason::UnknownWitnessMethodConformance); |
| auto conf = confResult.getValue(); |
| auto &module = wmi->getModule(); |
| SILFunction *fn = |
| module.lookUpFunctionInWitnessTable(conf, wmi->getMember()).first; |
| // If we were able to resolve it, then we can proceed. |
| if (fn) |
| return SymbolicValue::getFunction(fn); |
| |
| LLVM_DEBUG(llvm::dbgs() |
| << "ConstExpr Unresolved witness: " << *value << "\n"); |
| return getUnknown(evaluator, value, UnknownReason::NoWitnesTableEntry); |
| } |
| |
| if (auto *builtin = dyn_cast<BuiltinInst>(value)) |
| return computeConstantValueBuiltin(builtin); |
| |
| if (auto *enumVal = dyn_cast<EnumInst>(value)) { |
| if (!enumVal->hasOperand()) |
| return SymbolicValue::getEnum(enumVal->getElement()); |
| |
| auto payload = getConstantValue(enumVal->getOperand()); |
| if (!payload.isConstant()) |
| return payload; |
| return SymbolicValue::getEnumWithPayload(enumVal->getElement(), payload, |
| evaluator.getAllocator()); |
| } |
| |
| // This one returns the address of its enum payload. |
| if (auto *dai = dyn_cast<UncheckedTakeEnumDataAddrInst>(value)) { |
| auto enumVal = getConstAddrAndLoadResult(dai->getOperand()); |
| if (!enumVal.isConstant()) |
| return enumVal; |
| return createMemoryObject(value, enumVal.getEnumPayloadValue()); |
| } |
| |
| if (isa<SelectEnumInst>(value) || isa<SelectEnumAddrInst>(value)) { |
| SelectEnumInstBase *selectInst = dyn_cast<SelectEnumInst>(value); |
| if (!selectInst) { |
| selectInst = dyn_cast<SelectEnumAddrInst>(value); |
| } |
| |
| SILValue enumOperand = selectInst->getEnumOperand(); |
| SymbolicValue enumValue = isa<SelectEnumInst>(selectInst) |
| ? getConstantValue(enumOperand) |
| : getConstAddrAndLoadResult(enumOperand); |
| if (!enumValue.isConstant()) |
| return enumValue; |
| |
| assert(enumValue.getKind() == SymbolicValue::Enum || |
| enumValue.getKind() == SymbolicValue::EnumWithPayload); |
| |
| SILValue resultOperand = |
| selectInst->getCaseResult(enumValue.getEnumValue()); |
| return getConstantValue(resultOperand); |
| } |
| |
| // This instruction is a marker that returns its first operand. |
| if (auto *bai = dyn_cast<BeginAccessInst>(value)) |
| return getConstantValue(bai->getOperand()); |
| |
| // Look through copy_value and begin_borrow since the interpreter doesn't |
| // model these memory management instructions. |
| if (isa<CopyValueInst>(value) || isa<BeginBorrowInst>(value)) |
| return getConstantValue(cast<SingleValueInstruction>(value)->getOperand(0)); |
| |
| // Builtin.RawPointer and addresses have the same representation. |
| if (auto *p2ai = dyn_cast<PointerToAddressInst>(value)) |
| return getConstantValue(p2ai->getOperand()); |
| |
| // Indexing a pointer moves the deepest index of the access path it represents |
| // within a memory object. For example, if a pointer p represents the access |
| // path [1, 2] within a memory object, p + 1 represents [1, 3] |
| if (auto *ia = dyn_cast<IndexAddrInst>(value)) { |
| auto index = getConstantValue(ia->getOperand(1)); |
| if (!index.isConstant()) |
| return index; |
| auto basePtr = getConstantValue(ia->getOperand(0)); |
| if (basePtr.getKind() != SymbolicValue::Address) |
| return basePtr; |
| |
| SmallVector<unsigned, 4> accessPath; |
| auto *memObject = basePtr.getAddressValue(accessPath); |
| assert(!accessPath.empty() && "Can't index a non-indexed address"); |
| accessPath.back() += index.getIntegerValue().getLimitedValue(); |
| return SymbolicValue::getAddress(memObject, accessPath, |
| evaluator.getAllocator()); |
| } |
| |
| LLVM_DEBUG(llvm::dbgs() << "ConstExpr Unknown simple: " << *value << "\n"); |
| |
| // Otherwise, we don't know how to handle this. |
| auto unknownReason = isa<SingleValueInstruction>(value) |
| ? UnknownReason::UnsupportedInstruction |
| : UnknownReason::Default; |
| return getUnknown(evaluator, value, unknownReason); |
| } |
| |
| SymbolicValue |
| ConstExprFunctionState::computeConstantValueBuiltin(BuiltinInst *inst) { |
| const BuiltinInfo &builtin = inst->getBuiltinInfo(); |
| |
| // Constant builtins. |
| if (inst->getNumOperands() == 0) { |
| switch (builtin.ID) { |
| default: |
| break; |
| case BuiltinValueKind::AssertConf: |
| return SymbolicValue::getInteger(evaluator.getAssertConfig(), 32); |
| } |
| } |
| |
| // Handle various cases in groups. |
| auto invalidOperandValue = [&]() -> SymbolicValue { |
| return getUnknown(evaluator, SILValue(inst), |
| UnknownReason::InvalidOperandValue); |
| }; |
| |
| // Unary operations. |
| if (inst->getNumOperands() == 1) { |
| auto operand = getConstantValue(inst->getOperand(0)); |
| // TODO: Could add a "value used here" sort of diagnostic. |
| if (!operand.isConstant()) |
| return operand; |
| |
| // Implement support for s_to_s_checked_trunc_Int2048_Int64 and other |
| // checking integer truncates. These produce a tuple of the result value |
| // and an overflow bit. |
| // |
| // TODO: We can/should diagnose statically detectable integer overflow |
| // errors and subsume the ConstantFolding.cpp mandatory SIL pass. |
| auto IntCheckedTruncFn = [&](bool srcSigned, |
| bool dstSigned) -> SymbolicValue { |
| if (operand.getKind() != SymbolicValue::Integer) |
| return invalidOperandValue(); |
| |
| APInt operandVal = operand.getIntegerValue(); |
| uint32_t srcBitWidth = operandVal.getBitWidth(); |
| auto dstBitWidth = |
| builtin.Types[1]->castTo<BuiltinIntegerType>()->getGreatestWidth(); |
| |
| // Note that the if the source type is a Builtin.IntLiteral, operandVal |
| // could have fewer bits than the destination bit width and may only |
| // require a sign extension. |
| APInt result = operandVal.sextOrTrunc(dstBitWidth); |
| |
| // Determine if there is a overflow. |
| if (operandVal.getBitWidth() > dstBitWidth) { |
| // Re-extend the value back to its source and check for loss of value. |
| APInt reextended = |
| dstSigned ? result.sext(srcBitWidth) : result.zext(srcBitWidth); |
| bool overflowed = (operandVal != reextended); |
| |
| if (!srcSigned && dstSigned) |
| overflowed |= result.isSignBitSet(); |
| |
| if (overflowed) |
| return getUnknown(evaluator, SILValue(inst), UnknownReason::Overflow); |
| } |
| |
| auto &allocator = evaluator.getAllocator(); |
| // Build the Symbolic value result for our truncated value. |
| return SymbolicValue::getAggregate( |
| {SymbolicValue::getInteger(result, allocator), |
| SymbolicValue::getInteger(APInt(1, false), allocator)}, |
| allocator); |
| }; |
| |
| switch (builtin.ID) { |
| default: |
| break; |
| case BuiltinValueKind::SToSCheckedTrunc: |
| return IntCheckedTruncFn(true, true); |
| case BuiltinValueKind::UToSCheckedTrunc: |
| return IntCheckedTruncFn(false, true); |
| case BuiltinValueKind::SToUCheckedTrunc: |
| return IntCheckedTruncFn(true, false); |
| case BuiltinValueKind::UToUCheckedTrunc: |
| return IntCheckedTruncFn(false, false); |
| |
| case BuiltinValueKind::Trunc: |
| case BuiltinValueKind::TruncOrBitCast: |
| case BuiltinValueKind::ZExt: |
| case BuiltinValueKind::ZExtOrBitCast: |
| case BuiltinValueKind::SExt: |
| case BuiltinValueKind::SExtOrBitCast: { |
| if (operand.getKind() != SymbolicValue::Integer) |
| return invalidOperandValue(); |
| |
| unsigned destBitWidth = |
| inst->getType().castTo<BuiltinIntegerType>()->getGreatestWidth(); |
| |
| APInt result = operand.getIntegerValue(); |
| if (result.getBitWidth() != destBitWidth) { |
| switch (builtin.ID) { |
| default: |
| assert(0 && "Unknown case"); |
| case BuiltinValueKind::Trunc: |
| case BuiltinValueKind::TruncOrBitCast: |
| result = result.trunc(destBitWidth); |
| break; |
| case BuiltinValueKind::ZExt: |
| case BuiltinValueKind::ZExtOrBitCast: |
| result = result.zext(destBitWidth); |
| break; |
| case BuiltinValueKind::SExt: |
| case BuiltinValueKind::SExtOrBitCast: |
| result = result.sext(destBitWidth); |
| break; |
| } |
| } |
| return SymbolicValue::getInteger(result, evaluator.getAllocator()); |
| } |
| // The two following builtins are supported only for string constants. This |
| // is because this builtin is used by StaticString which is used in |
| // preconditions and assertion failures. Supporting this enables the |
| // evaluator to handle assertion/precondition failures. |
| case BuiltinValueKind::PtrToInt: |
| case BuiltinValueKind::IntToPtr: { |
| if (operand.getKind() != SymbolicValue::String) { |
| return getUnknown(evaluator, SILValue(inst), |
| UnknownReason::UnsupportedInstruction); |
| } |
| return operand; |
| } |
| } |
| } |
| |
| // Binary operations. |
| if (inst->getNumOperands() == 2) { |
| auto operand0 = getConstantValue(inst->getOperand(0)); |
| auto operand1 = getConstantValue(inst->getOperand(1)); |
| if (!operand0.isConstant()) |
| return operand0; |
| if (!operand1.isConstant()) |
| return operand1; |
| |
| auto constFoldIntCompare = |
| [&](const std::function<bool(const APInt &, const APInt &)> &fn) |
| -> SymbolicValue { |
| if (operand0.getKind() != SymbolicValue::Integer || |
| operand1.getKind() != SymbolicValue::Integer) |
| return invalidOperandValue(); |
| |
| auto result = fn(operand0.getIntegerValue(), operand1.getIntegerValue()); |
| return SymbolicValue::getInteger(APInt(1, result), |
| evaluator.getAllocator()); |
| }; |
| |
| #define REQUIRE_KIND(KIND) \ |
| if (operand0.getKind() != SymbolicValue::KIND || \ |
| operand1.getKind() != SymbolicValue::KIND) \ |
| return invalidOperandValue(); |
| |
| switch (builtin.ID) { |
| default: |
| break; |
| #define INT_BINOP(OPCODE, EXPR) \ |
| case BuiltinValueKind::OPCODE: { \ |
| REQUIRE_KIND(Integer) \ |
| auto l = operand0.getIntegerValue(), r = operand1.getIntegerValue(); \ |
| return SymbolicValue::getInteger((EXPR), evaluator.getAllocator()); \ |
| } |
| INT_BINOP(Add, l + r) |
| INT_BINOP(And, l & r) |
| INT_BINOP(AShr, l.ashr(r)) |
| INT_BINOP(LShr, l.lshr(r)) |
| INT_BINOP(Or, l | r) |
| INT_BINOP(Mul, l * r) |
| INT_BINOP(SDiv, l.sdiv(r)) |
| INT_BINOP(Shl, l << r) |
| INT_BINOP(SRem, l.srem(r)) |
| INT_BINOP(Sub, l - r) |
| INT_BINOP(UDiv, l.udiv(r)) |
| INT_BINOP(URem, l.urem(r)) |
| INT_BINOP(Xor, l ^ r) |
| #undef INT_BINOP |
| |
| #define INT_COMPARE(OPCODE, EXPR) \ |
| case BuiltinValueKind::OPCODE: \ |
| REQUIRE_KIND(Integer) \ |
| return constFoldIntCompare( \ |
| [&](const APInt &l, const APInt &r) -> bool { return (EXPR); }) |
| INT_COMPARE(ICMP_EQ, l == r); |
| INT_COMPARE(ICMP_NE, l != r); |
| INT_COMPARE(ICMP_SLT, l.slt(r)); |
| INT_COMPARE(ICMP_SGT, l.sgt(r)); |
| INT_COMPARE(ICMP_SLE, l.sle(r)); |
| INT_COMPARE(ICMP_SGE, l.sge(r)); |
| INT_COMPARE(ICMP_ULT, l.ult(r)); |
| INT_COMPARE(ICMP_UGT, l.ugt(r)); |
| INT_COMPARE(ICMP_ULE, l.ule(r)); |
| INT_COMPARE(ICMP_UGE, l.uge(r)); |
| #undef INT_COMPARE |
| #undef REQUIRE_KIND |
| |
| case BuiltinValueKind::Expect: |
| return operand0; |
| } |
| } |
| |
| // Three operand builtins. |
| if (inst->getNumOperands() == 3) { |
| auto operand0 = getConstantValue(inst->getOperand(0)); |
| auto operand1 = getConstantValue(inst->getOperand(1)); |
| auto operand2 = getConstantValue(inst->getOperand(2)); |
| if (!operand0.isConstant()) |
| return operand0; |
| if (!operand1.isConstant()) |
| return operand1; |
| if (!operand2.isConstant()) |
| return operand2; |
| |
| // Overflowing integer operations like sadd_with_overflow take three |
| // operands: the last one is a "should report overflow" bit. |
| auto constFoldIntOverflow = |
| [&](const std::function<APInt(const APInt &, const APInt &, bool &)> |
| &fn) -> SymbolicValue { |
| if (operand0.getKind() != SymbolicValue::Integer || |
| operand1.getKind() != SymbolicValue::Integer || |
| operand2.getKind() != SymbolicValue::Integer) |
| return invalidOperandValue(); |
| |
| auto l = operand0.getIntegerValue(), r = operand1.getIntegerValue(); |
| bool overflowed = false; |
| auto result = fn(l, r, overflowed); |
| |
| // Return a statically diagnosed overflow if the operation is supposed to |
| // trap on overflow. |
| if (overflowed && !operand2.getIntegerValue().isNullValue()) |
| return getUnknown(evaluator, SILValue(inst), UnknownReason::Overflow); |
| |
| auto &allocator = evaluator.getAllocator(); |
| // Build the Symbolic value result for our normal and overflow bit. |
| return SymbolicValue::getAggregate( |
| {SymbolicValue::getInteger(result, allocator), |
| SymbolicValue::getInteger(APInt(1, overflowed), allocator)}, |
| allocator); |
| }; |
| |
| switch (builtin.ID) { |
| default: |
| break; |
| |
| #define INT_OVERFLOW(OPCODE, METHOD) \ |
| case BuiltinValueKind::OPCODE: \ |
| return constFoldIntOverflow( \ |
| [&](const APInt &l, const APInt &r, bool &overflowed) -> APInt { \ |
| return l.METHOD(r, overflowed); \ |
| }) |
| INT_OVERFLOW(SAddOver, sadd_ov); |
| INT_OVERFLOW(UAddOver, uadd_ov); |
| INT_OVERFLOW(SSubOver, ssub_ov); |
| INT_OVERFLOW(USubOver, usub_ov); |
| INT_OVERFLOW(SMulOver, smul_ov); |
| INT_OVERFLOW(UMulOver, umul_ov); |
| #undef INT_OVERFLOW |
| } |
| } |
| |
| LLVM_DEBUG(llvm::dbgs() << "ConstExpr Unknown Builtin: " << *inst << "\n"); |
| |
| // Otherwise, we don't know how to handle this builtin. |
| return getUnknown(evaluator, SILValue(inst), |
| UnknownReason::UnsupportedInstruction); |
| } |
| |
| // Handle calls to opaque callees, either by handling them and returning None or |
| // by returning with a Unknown indicating a failure. |
| llvm::Optional<SymbolicValue> |
| ConstExprFunctionState::computeOpaqueCallResult(ApplyInst *apply, |
| SILFunction *callee) { |
| LLVM_DEBUG(llvm::dbgs() << "ConstExpr Opaque Callee: " << *callee << "\n"); |
| return evaluator.getUnknown( |
| (SILInstruction *)apply, |
| UnknownReason::createCalleeImplementationUnknown(callee)); |
| } |
| |
| /// Given a symbolic value representing an instance of StaticString, look into |
| /// the aggregate and extract the static string value stored inside it. |
| static Optional<StringRef> |
| extractStaticStringValue(SymbolicValue staticString) { |
| if (staticString.getKind() != SymbolicValue::Aggregate) |
| return None; |
| ArrayRef<SymbolicValue> staticStringProps = staticString.getAggregateValue(); |
| if (staticStringProps.empty() || |
| staticStringProps[0].getKind() != SymbolicValue::String) |
| return None; |
| return staticStringProps[0].getStringValue(); |
| } |
| |
| /// If the specified type is a Swift.Array of some element type, then return the |
| /// element type. Otherwise, return a null Type. |
| static Type getArrayElementType(Type ty) { |
| if (auto bgst = ty->getAs<BoundGenericStructType>()) |
| if (bgst->getDecl() == bgst->getASTContext().getArrayDecl()) |
| return bgst->getGenericArgs()[0]; |
| return Type(); |
| } |
| |
| /// Given a call to a well known function, collect its arguments as constants, |
| /// fold it, and return None. If any of the arguments are not constants, marks |
| /// the call's results as Unknown, and return an Unknown with information about |
| /// the error. |
| llvm::Optional<SymbolicValue> |
| ConstExprFunctionState::computeWellKnownCallResult(ApplyInst *apply, |
| WellKnownFunction callee) { |
| auto conventions = apply->getSubstCalleeConv(); |
| switch (callee) { |
| case WellKnownFunction::AssertionFailure: { |
| SmallString<4> message; |
| for (unsigned i = 0; i < apply->getNumArguments(); i++) { |
| SILValue argument = apply->getArgument(i); |
| SymbolicValue argValue = getConstantValue(argument); |
| Optional<StringRef> stringOpt = extractStaticStringValue(argValue); |
| |
| // The first argument is a prefix that specifies the kind of failure |
| // this is. |
| if (i == 0) { |
| if (stringOpt) { |
| message += stringOpt.getValue(); |
| } else { |
| // Use a generic prefix here, as the actual prefix is not a constant. |
| message += "assertion failed"; |
| } |
| continue; |
| } |
| |
| if (stringOpt) { |
| message += ": "; |
| message += stringOpt.getValue(); |
| } |
| } |
| return evaluator.getUnknown( |
| (SILInstruction *)apply, |
| UnknownReason::createTrap(message, evaluator.getAllocator())); |
| } |
| case WellKnownFunction::ArrayInitEmpty: { // Array.init() |
| assert(conventions.getNumDirectSILResults() == 1 && |
| conventions.getNumIndirectSILResults() == 0 && |
| "unexpected Array.init() signature"); |
| |
| auto typeValue = getConstantValue(apply->getOperand(1)); |
| if (typeValue.getKind() != SymbolicValue::Metatype) { |
| return typeValue.isConstant() |
| ? getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::InvalidOperandValue) |
| : typeValue; |
| } |
| Type arrayType = typeValue.getMetatypeValue(); |
| |
| // Create an empty SymbolicArrayStorage and then create a SymbolicArray |
| // using it. |
| SymbolicValue arrayStorage = SymbolicValue::getSymbolicArrayStorage( |
| {}, getArrayElementType(arrayType)->getCanonicalType(), |
| evaluator.getAllocator()); |
| auto arrayVal = SymbolicValue::getArray(arrayType, arrayStorage, |
| evaluator.getAllocator()); |
| setValue(apply, arrayVal); |
| return None; |
| } |
| case WellKnownFunction::AllocateUninitializedArray: { |
| // This function has this signature: |
| // func _allocateUninitializedArray<Element>(_ builtinCount: Builtin.Word) |
| // -> (Array<Element>, Builtin.RawPointer) |
| assert(conventions.getNumParameters() == 1 && |
| conventions.getNumDirectSILResults() == 2 && |
| conventions.getNumIndirectSILResults() == 0 && |
| "unexpected _allocateUninitializedArray signature"); |
| |
| // Figure out the allocation size. |
| auto numElementsSV = getConstantValue(apply->getOperand(1)); |
| if (!numElementsSV.isConstant()) |
| return numElementsSV; |
| |
| unsigned numElements = numElementsSV.getIntegerValue().getLimitedValue(); |
| |
| // Allocating uninitialized arrays is supported only in flow-sensitive mode. |
| // TODO: the top-level mode in the interpreter should be phased out. |
| if (!fn) |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::Default); |
| |
| SmallVector<SymbolicValue, 8> elementConstants; |
| // Set array elements to uninitialized state. Subsequent stores through |
| // their addresses will initialize the elements. |
| elementConstants.assign(numElements, SymbolicValue::getUninitMemory()); |
| |
| Type arrayType = apply->getType().castTo<TupleType>()->getElementType(0); |
| Type arrayEltType = getArrayElementType(arrayType); |
| assert(arrayEltType && "Couldn't understand Swift.Array type?"); |
| |
| // Create a SymbolicArrayStorage with \c elements and then create a |
| // SymbolicArray using it. |
| SymbolicValueAllocator &allocator = evaluator.getAllocator(); |
| SymbolicValue arrayStorage = SymbolicValue::getSymbolicArrayStorage( |
| elementConstants, arrayEltType->getCanonicalType(), allocator); |
| SymbolicValue array = |
| SymbolicValue::getArray(arrayType, arrayStorage, allocator); |
| |
| // Construct return value for this call, which is a pair consisting of the |
| // address of the first element of the array and the array. |
| SymbolicValue storageAddress = array.getAddressOfArrayElement(allocator, 0); |
| setValue(apply, |
| SymbolicValue::getAggregate({array, storageAddress}, allocator)); |
| return None; |
| } |
| case WellKnownFunction::ArrayAppendElement: { |
| // This function has the following signature in SIL: |
| // (@in Element, @inout Array<Element>) -> () |
| assert(conventions.getNumParameters() == 2 && |
| conventions.getNumDirectSILResults() == 0 && |
| conventions.getNumIndirectSILResults() == 0 && |
| "unexpected Array.append(_:) signature"); |
| // Get the element to be appended which is passed indirectly (@in). |
| SymbolicValue elementAddress = getConstantValue(apply->getOperand(1)); |
| if (!elementAddress.isConstant()) |
| return elementAddress; |
| |
| auto invalidOperand = [&]() { |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::InvalidOperandValue); |
| }; |
| if (elementAddress.getKind() != SymbolicValue::Address) { |
| // TODO: store the operand number in the error message here. |
| return invalidOperand(); |
| } |
| |
| SmallVector<unsigned, 4> elementAP; |
| SymbolicValue element = |
| elementAddress.getAddressValue(elementAP)->getValue(); |
| |
| // Get the array value. The array is passed @inout. |
| SymbolicValue arrayAddress = getConstantValue(apply->getOperand(2)); |
| if (!arrayAddress.isConstant()) |
| return arrayAddress; |
| if (arrayAddress.getKind() != SymbolicValue::Address) |
| return invalidOperand(); |
| |
| SmallVector<unsigned, 4> arrayAP; |
| SymbolicValueMemoryObject *arrayMemoryObject = |
| arrayAddress.getAddressValue(arrayAP); |
| SymbolicValue arrayValue = arrayMemoryObject->getValue(); |
| if (arrayValue.getKind() != SymbolicValue::Array) { |
| return invalidOperand(); |
| } |
| |
| // Create a new array storage by appending the \c element to the existing |
| // storage, and create a new array using the new storage. |
| SymbolicValue arrayStorage = arrayValue.getStorageOfArray(); |
| CanType elementType; |
| ArrayRef<SymbolicValue> oldElements = |
| arrayStorage.getStoredElements(elementType); |
| SmallVector<SymbolicValue, 4> newElements(oldElements.begin(), |
| oldElements.end()); |
| newElements.push_back(element); |
| |
| SymbolicValueAllocator &allocator = evaluator.getAllocator(); |
| SymbolicValue newStorage = SymbolicValue::getSymbolicArrayStorage( |
| newElements, elementType, allocator); |
| SymbolicValue newArray = SymbolicValue::getArray(arrayValue.getArrayType(), |
| newStorage, allocator); |
| arrayMemoryObject->setIndexedElement(arrayAP, newArray, allocator); |
| return None; |
| } |
| case WellKnownFunction::StringInitEmpty: { // String.init() |
| assert(conventions.getNumDirectSILResults() == 1 && |
| conventions.getNumIndirectSILResults() == 0 && |
| "unexpected String.init() signature"); |
| auto result = SymbolicValue::getString("", evaluator.getAllocator()); |
| setValue(apply, result); |
| return None; |
| } |
| case WellKnownFunction::StringMakeUTF8: { |
| // String.init(_builtinStringLiteral start: Builtin.RawPointer, |
| // utf8CodeUnitCount: Builtin.Word, |
| // isASCII: Builtin.Int1) |
| assert(conventions.getNumDirectSILResults() == 1 && |
| conventions.getNumIndirectSILResults() == 0 && |
| conventions.getNumParameters() == 4 && "unexpected signature"); |
| auto literal = getConstantValue(apply->getOperand(1)); |
| if (literal.getKind() != SymbolicValue::String) { |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::InvalidOperandValue); |
| } |
| auto literalVal = literal.getStringValue(); |
| |
| auto byteCount = getConstantValue(apply->getOperand(2)); |
| if (byteCount.getKind() != SymbolicValue::Integer || |
| byteCount.getIntegerValue().getLimitedValue() != literalVal.size()) { |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::InvalidOperandValue); |
| } |
| setValue(apply, literal); |
| return None; |
| } |
| case WellKnownFunction::StringAppend: { |
| // static String.append (_: String, _: inout String) |
| assert(conventions.getNumDirectSILResults() == 0 && |
| conventions.getNumIndirectSILResults() == 0 && |
| conventions.getNumParameters() == 2 && |
| "unexpected String.append() signature"); |
| |
| auto otherString = getConstantValue(apply->getOperand(1)); |
| if (!otherString.isConstant()) { |
| return otherString; |
| } |
| if (otherString.getKind() != SymbolicValue::String) { |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::InvalidOperandValue); |
| } |
| |
| auto inoutOperand = apply->getOperand(2); |
| auto firstString = getConstAddrAndLoadResult(inoutOperand); |
| if (!firstString.isConstant()) { |
| return firstString; |
| } |
| if (firstString.getKind() != SymbolicValue::String) { |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::InvalidOperandValue); |
| } |
| |
| auto result = SmallString<8>(firstString.getStringValue()); |
| result.append(otherString.getStringValue()); |
| auto resultVal = SymbolicValue::getString(result, evaluator.getAllocator()); |
| computeFSStore(resultVal, inoutOperand); |
| return None; |
| } |
| case WellKnownFunction::StringEquals: { |
| // static String.== infix(_: String, _: String) |
| assert(conventions.getNumDirectSILResults() == 1 && |
| conventions.getNumIndirectSILResults() == 0 && |
| conventions.getNumParameters() == 3 && |
| "unexpected String.==() signature"); |
| |
| auto firstString = getConstantValue(apply->getOperand(1)); |
| if (firstString.getKind() != SymbolicValue::String) { |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::InvalidOperandValue); |
| } |
| |
| auto otherString = getConstantValue(apply->getOperand(2)); |
| if (otherString.getKind() != SymbolicValue::String) { |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::InvalidOperandValue); |
| } |
| |
| // The result is a Swift.Bool which is a struct that wraps an Int1. |
| int isEqual = firstString.getStringValue() == otherString.getStringValue(); |
| auto intVal = |
| SymbolicValue::getInteger(APInt(1, isEqual), evaluator.getAllocator()); |
| auto result = SymbolicValue::getAggregate(ArrayRef<SymbolicValue>(intVal), |
| evaluator.getAllocator()); |
| setValue(apply, result); |
| return None; |
| } |
| case WellKnownFunction::StringEscapePercent: { |
| // String.percentEscapedString.getter |
| assert(conventions.getNumDirectSILResults() == 1 && |
| conventions.getNumIndirectSILResults() == 0 && |
| conventions.getNumParameters() == 1 && |
| "unexpected String.percentEscapedString signature"); |
| |
| auto stringArgument = getConstantValue(apply->getOperand(1)); |
| if (!stringArgument.isConstant()) { |
| return stringArgument; |
| } |
| |
| if (stringArgument.getKind() != SymbolicValue::String) { |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::InvalidOperandValue); |
| } |
| |
| // Replace all precent symbol (%) in the string with double percents (%%) |
| StringRef stringVal = stringArgument.getStringValue(); |
| SmallString<4> percentEscapedString; |
| for (auto charElem : stringVal) { |
| percentEscapedString.push_back(charElem); |
| if (charElem == '%') { |
| percentEscapedString.push_back('%'); |
| } |
| } |
| |
| auto resultVal = SymbolicValue::getString(percentEscapedString.str(), |
| evaluator.getAllocator()); |
| setValue(apply, resultVal); |
| return None; |
| } |
| } |
| llvm_unreachable("unhandled WellKnownFunction"); |
| } |
| |
| /// Given a call to a function, determine whether it is a call to a constexpr |
| /// function. If so, collect its arguments as constants, fold it and return |
| /// None. If not, mark the results as Unknown, and return an Unknown with |
| /// information about the error. |
| llvm::Optional<SymbolicValue> |
| ConstExprFunctionState::computeCallResult(ApplyInst *apply) { |
| // Determine the callee. |
| auto calleeFn = getConstantValue(apply->getOperand(0)); |
| if (calleeFn.getKind() != SymbolicValue::Function) |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::InvalidOperandValue); |
| |
| SILFunction *callee = calleeFn.getFunctionValue(); |
| evaluator.recordCalledFunctionIfEnabled(callee); |
| |
| // If this is a well-known function, do not step into it. |
| if (auto wellKnownFunction = classifyFunction(callee)) |
| return computeWellKnownCallResult(apply, *wellKnownFunction); |
| |
| // Verify that we can fold all of the arguments to the call. |
| SmallVector<SymbolicValue, 4> paramConstants; |
| for (unsigned i = 0, e = apply->getNumOperands() - 1; i != e; ++i) { |
| // If any of the arguments is a non-constant value, then we can't fold this |
| // call. |
| auto op = apply->getOperand(i + 1); |
| SymbolicValue argValue = getConstantValue(op); |
| if (!argValue.isConstant()) |
| return argValue; |
| paramConstants.push_back(argValue); |
| } |
| |
| // If we reached an external function that hasn't been deserialized yet, make |
| // sure to pull it in so we can see its body. If that fails, then we can't |
| // analyze the function. |
| if (callee->isExternalDeclaration()) { |
| callee->getModule().loadFunction(callee); |
| if (callee->isExternalDeclaration()) |
| return computeOpaqueCallResult(apply, callee); |
| } |
| |
| // Compute the substitution map for the callee, which maps from all of its |
| // generic requirements to concrete conformances and concrete types. |
| SubstitutionMap calleeSubMap; |
| |
| auto calleeFnType = callee->getLoweredFunctionType(); |
| assert( |
| !calleeFnType->hasSelfParam() || |
| !calleeFnType->getSelfInstanceType()->getClassOrBoundGenericClass() && |
| "class methods are not supported"); |
| if (calleeFnType->getGenericSignature()) { |
| // Get the substitution map of the call. This maps from the callee's space |
| // into the caller's world. Witness methods require additional work to |
| // compute a mapping that is valid for the callee. |
| SubstitutionMap callSubMap; |
| |
| if (calleeFnType->getRepresentation() == |
| SILFunctionType::Representation::WitnessMethod) { |
| auto protocol = |
| calleeFnType->getWitnessMethodConformance().getRequirement(); |
| // Compute a mapping that maps the Self type of the protocol given by |
| // 'requirement' to the concrete type available in the substitutionMap. |
| auto protoSelfToConcreteType = |
| apply->getSubstitutionMap().subst(substitutionMap); |
| // Get a concrete protocol conformance by using the mapping for the |
| // Self type of the requirement. |
| auto conf = protoSelfToConcreteType.lookupConformance( |
| protocol->getSelfInterfaceType()->getCanonicalType(), protocol); |
| if (!conf.hasValue()) |
| return getUnknown(evaluator, (SILInstruction *)apply, |
| UnknownReason::UnknownWitnessMethodConformance); |
| |
| callSubMap = getWitnessMethodSubstitutions( |
| apply->getModule(), ApplySite(apply), callee, conf.getValue()); |
| |
| /// Remark: If we ever start to care about evaluating classes, |
| /// getSubstitutionsForCallee() is the analogous mapping function we |
| /// should use to get correct mapping from caller to callee namespace. |
| /// Ideally, the function must be renamed as |
| /// getClassMethodSubstitutions(). |
| } else { |
| callSubMap = apply->getSubstitutionMap(); |
| } |
| |
| // The substitution map for the callee is the composition of the callers |
| // substitution map, which is always type/conformance to a concrete type |
| // or conformance, with the mapping introduced by the call itself. This |
| // ensures that the callee's substitution map can map from its type |
| // namespace back to concrete types and conformances. |
| calleeSubMap = callSubMap.subst(substitutionMap); |
| } |
| |
| // Now that we have successfully folded all of the parameters, we can evaluate |
| // the call. |
| evaluator.pushCallStack(apply->getLoc().getSourceLoc()); |
| SymbolicValue result; |
| auto callResult = evaluateAndCacheCall(*callee, calleeSubMap, paramConstants, |
| result, numInstEvaluated, evaluator); |
| evaluator.popCallStack(); |
| |
| // Return the error value the callee evaluation failed. |
| if (callResult.hasValue()) |
| return callResult.getValue(); |
| setValue(apply, result); |
| return None; |
| } |
| |
| SymbolicValue ConstExprFunctionState::getConstantValue(SILValue value) { |
| // Check to see if we already have an answer. |
| auto it = calculatedValues.find(value); |
| if (it != calculatedValues.end()) |
| return it->second; |
| |
| if (!recursivelyComputeValueIfNotInState) { |
| return getUnknown(evaluator, value, UnknownReason::UntrackedSILValue); |
| } |
| |
| // If the client is asking for the value of a stack object that hasn't been |
| // computed, and if we have to recursively compute it, the stack object must |
| // be a single store value. Since this is a very different computation, |
| // split it out to its own path. |
| if (value->getType().isAddress() && isa<AllocStackInst>(value)) { |
| return getSingleWriterAddressValue(value); |
| } |
| |
| if (auto *apply = dyn_cast<ApplyInst>(value)) { |
| auto callResult = computeCallResult(apply); |
| |
| // If this failed, return the error code. |
| if (callResult.hasValue()) |
| return callResult.getValue(); |
| |
| assert(calculatedValues.count(apply)); |
| return calculatedValues[apply]; |
| } |
| |
| // Compute the value of a normal single-value instructions based on its |
| // operands. |
| auto result = computeConstantValue(value); |
| |
| // If this is the top-level lazy interpreter, output a debug trace. |
| if (!fn) { |
| LLVM_DEBUG(llvm::dbgs() << "ConstExpr top level: "; value->dump()); |
| LLVM_DEBUG(llvm::dbgs() << " RESULT: "; result.dump()); |
| } |
| |
| setValue(value, result); |
| return result; |
| } |
| |
| /// This is a helper function for `getSingleWriterAddressValue`. Callers should |
| /// use `getSingleWriterAddressValue`. |
| /// |
| /// If `addr` has no writing uses, returns None. |
| /// |
| /// If the following conditions hold: |
| /// * `addr` points at uninitialized memory; |
| /// * there are write(s) to `addr` that, taken together, set the memory |
| /// exactly once (e.g. a single "store" to `addr` OR multiple "store"s to |
| /// different "tuple_element_addr"s of `addr`); and |
| /// * the writes' value(s) can be const-evaluated; |
| /// Then: initializes the memory at `addr` and returns None. |
| /// |
| /// Otherwise, sets the memory at `addr` to an unknown SymbolicValue, and |
| /// returns the unknown SymbolicValue. |
| /// |
| /// Additional side effects: In all cases, this function might cache address |
| /// values for `addr` and for addresses derived from `addr`. |
| /// |
| /// Precondition: An address for `addr`, or an address that `addr` is derived |
| /// from, must be cached in `computedValues`. |
| llvm::Optional<SymbolicValue> |
| ConstExprFunctionState::initializeAddressFromSingleWriter(SILValue addr) { |
| LLVM_DEBUG(llvm::dbgs() << "ConstExpr: initializeAddressFromSingleWriter " |
| << addr); |
| |
| SmallVector<unsigned, 4> accessPath; |
| auto *memoryObject = getConstantValue(addr).getAddressValue(accessPath); |
| |
| // If we detect instructions that initialize an aggregate piecewise, then we |
| // set this flag, which tells us to verify that the entire aggregate has been |
| // initialized. |
| bool mustCheckAggregateInitialized = false; |
| |
| // Sets the pointed-at memory to `value`. |
| auto setMemoryValue = [&](SymbolicValue value) { |
| memoryObject->setIndexedElement(accessPath, value, |
| evaluator.getAllocator()); |
| }; |
| |
| // Gets the pointed-at memory value. |
| auto getMemoryValue = [&]() -> SymbolicValue { |
| return memoryObject->getIndexedElement(accessPath); |
| }; |
| |
| // Does all error-condition side-effects, and returns the appropriate error |
| // result. |
| // Precondition: `unknown` must be an unknown SymbolicValue. |
| auto error = [&](SymbolicValue unknown) -> SymbolicValue { |
| assert(unknown.getKind() == SymbolicValue::Unknown); |
| setMemoryValue(unknown); |
| return unknown; |
| }; |
| |
| // Checks that the pointed-at aggregate is fully initialized. |
| // Precondition: The pointed-at memory value is uninit memory or an |
| // aggregate. |
| auto checkAggregateInitialized = [&]() -> bool { |
| auto memoryValue = getMemoryValue(); |
| return memoryValue.getKind() != SymbolicValue::UninitMemory && |
| llvm::all_of(memoryValue.getAggregateValue(), |
| [](SymbolicValue v) { return v.isConstant(); }); |
| }; |
| |
| // Okay, check out all of the users of this value looking for semantic stores |
| // into the address. If we find more than one, then this was a var or |
| // something else we can't handle. |
| // We must iterate over all uses, to make sure there is a single initializer. |
| // The only permitted early exit is when we know for sure that we have failed. |
| for (auto *use : addr->getUses()) { |
| auto user = use->getUser(); |
| |
| // Ignore markers, loads, and other things that aren't stores to this stack |
| // value. |
| if (isa<LoadInst>(user) || isa<DeallocStackInst>(user) || |
| isa<DestroyAddrInst>(user) || isa<DebugValueAddrInst>(user)) |
| continue; |
| |
| // TODO: Allow BeginAccess/EndAccess users. |
| |
| // If this is a store *to* the memory, analyze the input value. |
| if (auto *si = dyn_cast<StoreInst>(user)) { |
| if (use->getOperandNumber() == 1) { |
| // Forbid multiple assignment. |
| if (getMemoryValue().getKind() != SymbolicValue::UninitMemory) |
| return error(getUnknown(evaluator, addr, |
| UnknownReason::MutipleTopLevelWriters)); |
| |
| auto result = getConstantValue(si->getOperand(0)); |
| if (!result.isConstant()) |
| return error(result); |
| setMemoryValue(result); |
| continue; |
| } |
| } |
| |
| if (auto *cai = dyn_cast<CopyAddrInst>(user)) { |
| // If this is a copy_addr *from* the memory, then it is a load, ignore it. |
| if (use->getOperandNumber() == 0) |
| continue; |
| |
| // If this is a copy_addr *to* the memory, analyze the input value. |
| assert(use->getOperandNumber() == 1 && "copy_addr has two operands"); |
| |
| // Forbid multiple assignment. |
| if (getMemoryValue().getKind() != SymbolicValue::UninitMemory) |
| return error( |
| getUnknown(evaluator, addr, UnknownReason::MutipleTopLevelWriters)); |
| |
| auto result = getConstAddrAndLoadResult(cai->getOperand(0)); |
| if (!result.isConstant()) |
| return error(result); |
| |
| setMemoryValue(result); |
| continue; |
| } |
| |
| // If this is an apply_inst passing the memory address as an indirect |
| // result operand, then we have a call that fills in this result. |
| if (auto *apply = dyn_cast<ApplyInst>(user)) { |
| auto conventions = apply->getSubstCalleeConv(); |
| |
| // If this is an out-parameter, it is like a store. If not, this is an |
| // indirect read which is ok. |
| unsigned numIndirectResults = conventions.getNumIndirectSILResults(); |
| unsigned opNum = use->getOperandNumber() - 1; |
| if (opNum >= numIndirectResults) |
| continue; |
| |
| // Forbid multiple assignment. |
| if (getMemoryValue().getKind() != SymbolicValue::UninitMemory) |
| return error( |
| getUnknown(evaluator, addr, UnknownReason::MutipleTopLevelWriters)); |
| |
| // The callee needs to be a direct call to a constant expression. |
| auto callResult = computeCallResult(apply); |
| |
| // If the call failed, we're done. |
| if (callResult.hasValue()) |
| return error(*callResult); |
| |
| // computeCallResult will have figured out the result and cached it for |
| // us. |
| assert(getMemoryValue().isConstant()); |
| continue; |
| } |
| |
| // If it is an index_addr, make sure it is a different address from base. |
| if (auto *iai = dyn_cast<IndexAddrInst>(user)) { |
| assert(use->get() == iai->getBase()); |
| if (auto *ili = dyn_cast<IntegerLiteralInst>(iai->getIndex())) { |
| if (ili->getValue().getLimitedValue() != 0) |
| continue; |
| } |
| return error( |
| getUnknown(evaluator, addr, UnknownReason::NotTopLevelConstant)); |
| } |
| |
| if (auto *teai = dyn_cast<TupleElementAddrInst>(user)) { |
| // Try finding a writer among the users of `teai`. For example: |
| // %179 = alloc_stack $(Int32, Int32, Int32, Int32) |
| // %183 = tuple_element_addr %179 : $*(Int32, Int32, Int32, Int32), 3 |
| // copy_addr %114 to [initialization] %183 : $*Int32 |
| // %191 = tuple_element_addr %179 : $*(Int32, Int32, Int32, Int32), 3 |
| // copy_addr [take] %191 to [initialization] %178 : $*Int32 |
| // |
| // The workflow is: when const-evaluating %178, we const-evaluate %191, |
| // which in turn triggers const-evaluating %179, thereby enter this |
| // function, where `addrInst` being %179. Among its users, %191 is not an |
| // initializer, so we skip it (`initializeAddressFromSingleWriter(teai)` |
| // below will act as a no-op on it). %183 is a good initializer and can |
| // be const-evaluated (by const-evaluating %114). |
| |
| // We can't forbid multiple assignment here by checking for uninit memory, |
| // because previous TupleElementAddrInsts may have already partially |
| // initialized the memory. However, the recursive call to |
| // `initializeAddressFromSingleWriter` below detects and forbids multiple |
| // assignment, so we don't need to do it here. |
| |
| if (auto failure = initializeAddressFromSingleWriter(teai)) |
| return error(*failure); |
| |
| // If this instruction partially initialized the memory, then we must |
| // remember to check later that the memory has been fully initialized. |
| if (getMemoryValue().getKind() != SymbolicValue::UninitMemory) |
| mustCheckAggregateInitialized = true; |
| |
| #ifndef NDEBUG |
| // If all aggregate elements are const, we have successfully |
| // const-evaluated the entire tuple! |
| if (checkAggregateInitialized()) |
| LLVM_DEBUG(llvm::dbgs() << "Const-evaluated the entire tuple: "; |
| getMemoryValue().dump()); |
| #endif // NDEBUG |
| continue; |
| } |
| |
| LLVM_DEBUG(llvm::dbgs() |
| << "Unknown SingleStore ConstExpr user: " << *user << "\n"); |
| |
| // If this is some other user that we don't know about, then we should |
| // treat it conservatively, because it could store into the address. |
| return error( |
| getUnknown(evaluator, addr, UnknownReason::NotTopLevelConstant)); |
| } |
| |
| if (mustCheckAggregateInitialized && !checkAggregateInitialized()) |
| return error( |
| getUnknown(evaluator, addr, UnknownReason::NotTopLevelConstant)); |
| |
| return None; |
| } |
| |
| /// Find the initializer (single writer) of `addr` among it users, |
| /// const-evaluate it and store the result into a memory object. |
| /// |
| /// Side effects: Creates a fully-initialized memory object (on success), or a |
| /// memory object containing an unknown (on failure). Inserts the address of |
| /// that memory object into `calculatedValues`, with key `addr`. |
| /// |
| /// Returns the address of the memory object on success. Returns the unknown on |
| /// failure. |
| /// |
| /// Some use cases are: |
| /// 1. When analyzing the top-level code involved in a constant expression, we |
| /// can end up demanding values that are returned by address. Handle this by |
| /// finding the temporary stack value (an alloc_stack inst), and calling this |
| /// method on it. |
| /// 2. When const-evaluating an array via decodeAllocUninitializedArray(), |
| /// do that by const-evaluating the writers of individual array elements. |
| /// |
| /// There are a few forms of writers, such as: |
| /// - store %3 to %4 ... |
| /// - %8 = pointer_to_address %7 : $Builtin.RawPointer to [strict] $*Int32 |
| /// - %14 = index_addr %9 : $*Int32, %13 : $Builtin.Word |
| /// - %180 = tuple_element_addr %179 : $*(Int32, Int32, Int32, Int32), 3 |
| /// |
| /// Note unlike getConstAddrAndLoadResult(), this method does *not* |
| /// const-evaluate the input `addr` by evaluating its operand first, such as %7 |
| /// above. Instead, it finds a user of %8 who is the initializer, and uses that |
| /// to set the const value for %7. In other words, this method propagates const |
| /// info from result to operand (e.g. from %8 to %7), while |
| /// getConstAddrAndLoadResult() propagates const info from operand to result. |
| /// |
| /// As such, when const-evaluating an address-typed inst such as |
| /// pointer_to_address, if the address is to be written to, caller should call |
| /// this method (e.g. a[3] = 17). If the address is to be read (e.g. let v = |
| /// a[3]), call getConstAddrAndLoadResult(). |
| SymbolicValue |
| ConstExprFunctionState::getSingleWriterAddressValue(SILValue addr) { |
| // Check to see if we already have an answer. |
| auto it = calculatedValues.find(addr); |
| if (it != calculatedValues.end()) |
| return it->second; |
| |
| assert(addr->getType().isAddress()); |
| auto *addrInst = dyn_cast<SingleValueInstruction>(addr); |
| if (!addrInst) |
| return getUnknown(evaluator, addr, UnknownReason::NotTopLevelConstant); |
| |
| // Create a memory object to initialize, and point `addr` at it. |
| auto memoryAddress = |
| createMemoryObject(addr, SymbolicValue::getUninitMemory()); |
| auto *memoryObject = memoryAddress.getAddressValueMemoryObject(); |
| |
| if (auto failure = initializeAddressFromSingleWriter(addr)) { |
| assert(failure->getKind() == SymbolicValue::Unknown); |
| memoryObject->setValue(*failure); |
| return *failure; |
| } |
| if (!memoryObject->getValue().isConstant()) { |
| auto unknown = |
| getUnknown(evaluator, addr, UnknownReason::NotTopLevelConstant); |
| memoryObject->setValue(unknown); |
| return unknown; |
| } |
| |
| return memoryAddress; |
| } |
| |
| /// Given the operand to a load, resolve it to a constant if possible. |
| /// Also see the comments on getSingleWriterAddressValue() to contrast these 2 |
| /// APIs. |
| SymbolicValue ConstExprFunctionState::getConstAddrAndLoadResult(SILValue addr) { |
| auto addrVal = getConstantValue(addr); |
| if (!addrVal.isConstant()) |
| return addrVal; |
| |
| return loadAddrValue(addr, addrVal); |
| } |
| |
| /// Load and return the underlying (const) object whose address is given by |
| /// `addrVal`. On error, return a message based on `addr`. |
| SymbolicValue ConstExprFunctionState::loadAddrValue(SILValue addr, |
| SymbolicValue addrVal) { |
| SmallVector<unsigned, 4> accessPath; |
| auto *memoryObject = addrVal.getAddressValue(accessPath); |
| |
| // If this is a derived address, then we are digging into an aggregate |
| // value. |
| auto objectVal = memoryObject->getValue(); |
| |
| // Try digging through the aggregate to get to our value. |
| unsigned idx = 0, end = accessPath.size(); |
| while (idx != end && objectVal.getKind() == SymbolicValue::Aggregate) { |
| objectVal = objectVal.getAggregateValue()[accessPath[idx]]; |
| ++idx; |
| } |
| |
| // If we successfully indexed down to our value, then we're done. |
| if (idx == end) |
| return objectVal; |
| |
| // If the memory object had a reason, return it. |
| if (objectVal.isUnknown()) |
| return objectVal; |
| |
| // Otherwise, return a generic failure. |
| return getUnknown(evaluator, addr, UnknownReason::InvalidOperandValue); |
| } |
| |
| /// Evaluate a flow sensitive store to the specified pointer address. |
| llvm::Optional<SymbolicValue> |
| ConstExprFunctionState::computeFSStore(SymbolicValue storedCst, SILValue dest) { |
| // Only update existing memory locations that we're tracking. |
| auto it = calculatedValues.find(dest); |
| if (it == calculatedValues.end()) |
| return getUnknown(evaluator, dest, UnknownReason::UntrackedSILValue); |
| if (!it->second.isConstant()) |
| return getUnknown(evaluator, dest, UnknownReason::InvalidOperandValue); |
| |
| SmallVector<unsigned, 4> accessPath; |
| auto *memoryObject = it->second.getAddressValue(accessPath); |
| memoryObject->setIndexedElement(accessPath, storedCst, |
| evaluator.getAllocator()); |
| return None; |
| } |
| |
| /// Evaluate the specified instruction in a flow sensitive way, for use by |
| /// the constexpr function evaluator. This does not handle control flow |
| /// statements. This returns None on success, and an Unknown SymbolicValue with |
| /// information about an error on failure. |
| llvm::Optional<SymbolicValue> |
| ConstExprFunctionState::evaluateFlowSensitive(SILInstruction *inst) { |
| // These are just markers. |
| if (isa<DebugValueInst>(inst) || isa<DebugValueAddrInst>(inst) || |
| isa<EndAccessInst>(inst) || |
| // The interpreter doesn't model these memory management instructions, so |
| // skip them. |
| isa<DestroyAddrInst>(inst) || isa<RetainValueInst>(inst) || |
| isa<ReleaseValueInst>(inst) || isa<StrongRetainInst>(inst) || |
| isa<StrongReleaseInst>(inst) || isa<DestroyValueInst>(inst) || |
| isa<EndBorrowInst>(inst)) |
| return None; |
| |
| // If this is a special flow-sensitive instruction like a stack allocation, |
| // store, copy_addr, etc, we handle it specially here. |
| if (auto asi = dyn_cast<AllocStackInst>(inst)) { |
| // If a struct with no stored properties is created, no initialization is |
| // needed. Hence, create a empty aggregate as the initial value. |
| StructDecl *structDecl = |
| asi->getElementType().getStructOrBoundGenericStruct(); |
| if (structDecl && structDecl->getStoredProperties().empty()) { |
| createMemoryObject(asi, |
| SymbolicValue::getAggregate(ArrayRef<SymbolicValue>(), |
| evaluator.getAllocator())); |
| return None; |
| } |
| createMemoryObject(asi, SymbolicValue::getUninitMemory()); |
| return None; |
| } |
| |
| // If this is a deallocation of a memory object that we are tracking, then |
| // don't do anything. The memory is allocated in a BumpPtrAllocator so there |
| // is no useful way to free it. |
| if (isa<DeallocStackInst>(inst)) |
| return None; |
| |
| if (CondFailInst *condFail = dyn_cast<CondFailInst>(inst)) { |
| auto failed = getConstantValue(inst->getOperand(0)); |
| if (failed.getKind() == SymbolicValue::Integer) { |
| if (failed.getIntegerValue() == 0) |
| return None; |
| // Conditional fail actually failed. |
| return evaluator.getUnknown( |
| (SILInstruction *)inst, |
| UnknownReason::createTrap( |
| (Twine("trap: ") + condFail->getMessage()).str(), |
| evaluator.getAllocator())); |
| } |
| } |
| |
| // If this is a call, evaluate it. Calls are handled separately from other |
| // single-valued instructions because calls which return void will not be |
| // mapped to a symbolic value. Every other single-valued instruction will be |
| // mapped to a symbolic value if its evaluation is successful. |
| if (auto apply = dyn_cast<ApplyInst>(inst)) |
| return computeCallResult(apply); |
| |
| if (isa<StoreInst>(inst)) { |
| auto stored = getConstantValue(inst->getOperand(0)); |
| if (!stored.isConstant()) |
| return stored; |
| |
| return computeFSStore(stored, inst->getOperand(1)); |
| } |
| |
| // Copy addr is a load + store combination. |
| if (auto *copy = dyn_cast<CopyAddrInst>(inst)) { |
| auto value = getConstAddrAndLoadResult(copy->getOperand(0)); |
| if (!value.isConstant()) |
| return value; |
| |
| return computeFSStore(value, copy->getOperand(1)); |
| } |
| |
| if (auto *injectEnumInst = dyn_cast<InjectEnumAddrInst>(inst)) { |
| return computeFSStore(SymbolicValue::getEnum(injectEnumInst->getElement()), |
| injectEnumInst->getOperand()); |
| } |
| |
| // If the instruction produces a result, try computing it, and fail if the |
| // computation fails. |
| if (auto *singleValueInst = dyn_cast<SingleValueInstruction>(inst)) { |
| auto result = computeConstantValue(singleValueInst); |
| if (!result.isConstant()) |
| return result; |
| setValue(singleValueInst, result); |
| LLVM_DEBUG(llvm::dbgs() << " RESULT: "; result.dump()); |
| return None; |
| } |
| |
| if (isa<DestructureTupleInst>(inst) || isa<DestructureStructInst>(inst)) { |
| auto *mvi = cast<MultipleValueInstruction>(inst); |
| SymbolicValue aggVal = getConstantValue(mvi->getOperand(0)); |
| if (!aggVal.isConstant()) { |
| return aggVal; |
| } |
| assert(aggVal.getKind() == SymbolicValue::Aggregate); |
| |
| ArrayRef<SymbolicValue> aggElems = aggVal.getAggregateValue(); |
| assert(aggElems.size() == mvi->getNumResults()); |
| |
| for (unsigned i = 0; i < mvi->getNumResults(); ++i) { |
| setValue(mvi->getResult(i), aggElems[i]); |
| } |
| return None; |
| } |
| |
| LLVM_DEBUG(llvm::dbgs() << "ConstExpr Unknown FS: " << *inst << "\n"); |
| // If this is an unknown instruction with no results then bail out. |
| return getUnknown(evaluator, inst, UnknownReason::UnsupportedInstruction); |
| } |
| |
| std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>> |
| ConstExprFunctionState::evaluateInstructionAndGetNext( |
| SILBasicBlock::iterator instI, |
| SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks) { |
| |
| SILInstruction *inst = &*instI; |
| // If we can evaluate this flow sensitively, then return the next instruction. |
| if (!isa<TermInst>(inst)) { |
| auto fsResult = evaluateFlowSensitive(inst); |
| if (fsResult.hasValue()) |
| return {None, fsResult}; |
| return {++instI, None}; |
| } |
| |
| // If this is a branch instruction, evaluate and return the target basic block. |
| if (auto *br = dyn_cast<BranchInst>(inst)) { |
| auto destBB = br->getDestBB(); |
| |
| // If we've already visited this block then fail - we have a loop. |
| if (!visitedBlocks.insert(destBB).second) |
| return {None, getUnknown(evaluator, br, UnknownReason::Loop)}; |
| |
| // Set up basic block arguments. |
| for (unsigned i = 0, e = br->getNumArgs(); i != e; ++i) { |
| auto argument = getConstantValue(br->getArg(i)); |
| if (!argument.isConstant()) |
| return {None, argument}; |
| setValue(destBB->getArgument(i), argument); |
| } |
| // Set the instruction pointer to the first instruction of the block. |
| return {destBB->begin(), None}; |
| } |
| |
| if (auto *cbr = dyn_cast<CondBranchInst>(inst)) { |
| auto val = getConstantValue(inst->getOperand(0)); |
| if (!val.isConstant()) |
| return {None, val}; |
| |
| SILBasicBlock *destBB; |
| if (!val.getIntegerValue()) |
| destBB = cbr->getFalseBB(); |
| else |
| destBB = cbr->getTrueBB(); |
| |
| // If we've already visited this block then fail - we have a loop. |
| if (!visitedBlocks.insert(destBB).second) |
| return {None, getUnknown(evaluator, cbr, UnknownReason::Loop)}; |
| |
| return {destBB->begin(), None}; |
| } |
| |
| if (isa<SwitchEnumAddrInst>(inst) || isa<SwitchEnumInst>(inst)) { |
| SymbolicValue value; |
| SwitchEnumInstBase *switchInst = dyn_cast<SwitchEnumInst>(inst); |
| if (switchInst) { |
| value = getConstantValue(switchInst->getOperand()); |
| } else { |
| switchInst = cast<SwitchEnumAddrInst>(inst); |
| value = getConstAddrAndLoadResult(switchInst->getOperand()); |
| } |
| if (!value.isConstant()) |
| return {None, value}; |
| |
| assert(value.getKind() == SymbolicValue::Enum || |
| value.getKind() == SymbolicValue::EnumWithPayload); |
| |
| SILBasicBlock *caseBB = |
| switchInst->getCaseDestination(value.getEnumValue()); |
| if (caseBB->getNumArguments() == 0) |
| return {caseBB->begin(), None}; |
| |
| // Set up the arguments. |
| |
| // When there are multiple payload components, they form a single |
| // tuple-typed argument. |
| assert(caseBB->getNumArguments() == 1); |
| |
| if (caseBB->getParent()->hasOwnership() && |
| switchInst->getDefaultBBOrNull() == caseBB) { |
| // If we are visiting the default block and we are in ossa, then we may |
| // have uses of the failure parameter. That means we need to map the |
| // original value to the argument. |
| setValue(caseBB->getArgument(0), value); |
| return {caseBB->begin(), None}; |
| } |
| |
| assert(value.getKind() == SymbolicValue::EnumWithPayload); |
| auto argument = value.getEnumPayloadValue(); |
| assert(argument.isConstant()); |
| setValue(caseBB->getArgument(0), argument); |
| |
| return {caseBB->begin(), None}; |
| } |
| |
| LLVM_DEBUG(llvm::dbgs() << "ConstExpr: Unknown Branch Instruction: " << *inst |
| << "\n"); |
| |
| return {None, |
| getUnknown(evaluator, inst, UnknownReason::UnsupportedInstruction)}; |
| } |
| |
| /// Evaluate a call to the specified function as if it were a constant |
| /// expression, returning None and filling in `results` on success, or |
| /// returning an 'Unknown' SymbolicValue on failure carrying the error. |
| /// |
| static llvm::Optional<SymbolicValue> |
| evaluateAndCacheCall(SILFunction &fn, SubstitutionMap substitutionMap, |
| ArrayRef<SymbolicValue> arguments, SymbolicValue &result, |
| unsigned &numInstEvaluated, |
| ConstExprEvaluator &evaluator) { |
| assert(!fn.isExternalDeclaration() && "Can't analyze bodyless function"); |
| ConstExprFunctionState state(evaluator, &fn, substitutionMap, |
| numInstEvaluated, |
| /*TopLevelEvaluation*/ false); |
| |
| // TODO: implement caching. |
| // TODO: reject code that is too complex. |
| |
| unsigned nextBBArg = 0; |
| const auto &argList = fn.front().getArguments(); |
| |
| LLVM_DEBUG(llvm::dbgs().changeColor(raw_ostream::SAVEDCOLOR, /*bold*/ true) |
| << "\nConstExpr call fn: " |
| << Demangle::demangleSymbolAsString(fn.getName()); |
| llvm::dbgs().resetColor() << "\n"); |
| |
| assert(arguments.size() == argList.size() && "incorrect # arguments passed"); |
| for (auto argSymVal : arguments) |
| state.setValue(argList[nextBBArg++], argSymVal); |
| |
| // Keep track of which blocks we've already visited. We don't support loops |
| // and this allows us to reject them. |
| SmallPtrSet<SILBasicBlock *, 8> visitedBlocks; |
| |
| // Keep track of the current "instruction pointer". |
| SILBasicBlock::iterator nextInst = fn.front().begin(); |
| visitedBlocks.insert(&fn.front()); |
| |
| while (1) { |
| SILInstruction *inst = &*nextInst; |
| LLVM_DEBUG(llvm::dbgs() << "ConstExpr interpret: "; inst->dump()); |
| |
| // Make sure we haven't exceeded our interpreter iteration cap. |
| if (++numInstEvaluated > ConstExprLimit) { |
| return getUnknown(evaluator, inst, UnknownReason::TooManyInstructions); |
| } |
| |
| if (isa<ReturnInst>(inst)) { |
| auto val = state.getConstantValue(inst->getOperand(0)); |
| if (!val.isConstant()) |
| return val; |
| |
| // If we got a constant value, then we're good. Set up the normal result |
| // values as well as any indirect results. |
| result = val; |
| |
| // TODO: Handle caching of results. |
| |
| LLVM_DEBUG(llvm::dbgs() << "\n"); |
| return None; |
| } |
| |
| // Handle other instructions here. |
| Optional<SILBasicBlock::iterator> nextInstOpt = None; |
| Optional<SymbolicValue> errorVal = None; |
| |
| std::tie(nextInstOpt, errorVal) = |
| state.evaluateInstructionAndGetNext(nextInst, visitedBlocks); |
| |
| if (errorVal.hasValue()) |
| return errorVal; |
| |
| assert(nextInstOpt.hasValue()); |
| nextInst = nextInstOpt.getValue(); |
| } |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // ConstExprEvaluator implementation. |
| //===----------------------------------------------------------------------===// |
| |
| ConstExprEvaluator::ConstExprEvaluator(SymbolicValueAllocator &alloc, |
| unsigned assertConf, bool trackCallees) |
| : allocator(alloc), assertConfig(assertConf), trackCallees(trackCallees) {} |
| |
| ConstExprEvaluator::~ConstExprEvaluator() {} |
| |
| /// An explicit copy constructor. |
| ConstExprEvaluator::ConstExprEvaluator(const ConstExprEvaluator &other) |
| : allocator(other.allocator) { |
| callStack = other.callStack; |
| } |
| |
| SymbolicValue ConstExprEvaluator::getUnknown(SILNode *node, |
| UnknownReason reason) { |
| return SymbolicValue::getUnknown(node, reason, getCallStack(), |
| getAllocator()); |
| } |
| |
| /// 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 ConstExprEvaluator::computeConstantValues( |
| ArrayRef<SILValue> values, SmallVectorImpl<SymbolicValue> &results) { |
| unsigned numInstEvaluated = 0; |
| ConstExprFunctionState state(*this, /*SILFunction*/ nullptr, {}, |
| numInstEvaluated, |
| /*enableTopLevelEvaluation*/ true); |
| for (auto v : values) { |
| auto symVal = state.getConstantValue(v); |
| results.push_back(symVal); |
| |
| // Reset the execution limit back to zero for each subexpression we look |
| // at. We don't want lots of constants folded to trigger a limit. |
| numInstEvaluated = 0; |
| } |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // ConstExprStepEvaluator implementation. |
| //===----------------------------------------------------------------------===// |
| |
| ConstExprStepEvaluator::ConstExprStepEvaluator(SymbolicValueAllocator &alloc, |
| SILFunction *fun, |
| unsigned assertConf, |
| bool trackCallees) |
| : evaluator(alloc, assertConf, trackCallees), |
| internalState( |
| new ConstExprFunctionState(evaluator, fun, {}, stepsEvaluated, |
| /*enableTopLevelEvaluation*/ false)) { |
| assert(fun); |
| } |
| |
| ConstExprStepEvaluator::~ConstExprStepEvaluator() { delete internalState; } |
| |
| std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>> |
| ConstExprStepEvaluator::evaluate(SILBasicBlock::iterator instI) { |
| // Reset `stepsEvaluated` to zero. |
| stepsEvaluated = 0; |
| return internalState->evaluateInstructionAndGetNext(instI, visitedBlocks); |
| } |
| |
| std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>> |
| ConstExprStepEvaluator::skipByMakingEffectsNonConstant( |
| SILBasicBlock::iterator instI) { |
| SILInstruction *inst = &(*instI); |
| |
| // Set all constant state that could be mutated by the instruction |
| // to an unknown symbolic value. |
| for (auto &operand : inst->getAllOperands()) { |
| auto constValOpt = lookupConstValue(operand.get()); |
| if (!constValOpt) { |
| continue; |
| } |
| auto constVal = constValOpt.getValue(); |
| auto constKind = constVal.getKind(); |
| |
| // Skip can only be invoked on value types or addresses of value types. |
| // Note that adding a new kind of symbolic value may require handling its |
| // side-effects, especially if that symbolic value does not represent a |
| // value type. |
| assert(constKind == SymbolicValue::Address || |
| constKind == SymbolicValue::Unknown || |
| constKind == SymbolicValue::Metatype || |
| constKind == SymbolicValue::Function || |
| constKind == SymbolicValue::Integer || |
| constKind == SymbolicValue::String || |
| constKind == SymbolicValue::Aggregate || |
| constKind == SymbolicValue::Enum || |
| constKind == SymbolicValue::EnumWithPayload || |
| constKind == SymbolicValue::UninitMemory); |
| |
| if (constKind != SymbolicValue::Address) { |
| continue; |
| } |
| |
| // If the address is only used @in_guaranteed or @in_constant, there |
| // can be no mutation through this address. Therefore, ignore it. |
| if (ApplyInst *applyInst = dyn_cast<ApplyInst>(inst)) { |
| ApplySite applySite(applyInst); |
| SILArgumentConvention convention = |
| applySite.getArgumentConvention(operand); |
| if (convention == SILArgumentConvention::Indirect_In_Guaranteed || |
| convention == SILArgumentConvention::Indirect_In_Constant) { |
| continue; |
| } |
| } |
| |
| // Write an unknown value into the address. |
| SmallVector<unsigned, 4> accessPath; |
| auto *memoryObject = constVal.getAddressValue(accessPath); |
| auto unknownValue = SymbolicValue::getUnknown( |
| inst, |
| UnknownReason::create(UnknownReason::MutatedByUnevaluatedInstruction), |
| {}, evaluator.getAllocator()); |
| |
| auto memoryContent = memoryObject->getValue(); |
| if (memoryContent.getKind() == SymbolicValue::Aggregate) { |
| memoryObject->setIndexedElement(accessPath, unknownValue, |
| evaluator.getAllocator()); |
| } else { |
| memoryObject->setValue(unknownValue); |
| } |
| } |
| |
| // Map the results of this instruction to unknown values. |
| for (auto result : inst->getResults()) { |
| internalState->setValue( |
| result, SymbolicValue::getUnknown( |
| inst, |
| UnknownReason::create( |
| UnknownReason::ReturnedByUnevaluatedInstruction), |
| {}, evaluator.getAllocator())); |
| } |
| |
| // If we have a next instruction in the basic block return it. |
| // Otherwise, return None for the next instruction. |
| // Note that we can find the next instruction in the case of unconditional |
| // branches. But, there is no real need to do that as of now. |
| if (!isa<TermInst>(inst)) { |
| return {++instI, None}; |
| } |
| return {None, None}; |
| } |
| |
| bool ConstExprStepEvaluator::isFailStopError(SymbolicValue errorVal) { |
| assert(errorVal.isUnknown()); |
| |
| switch (errorVal.getUnknownReason().getKind()) { |
| case UnknownReason::TooManyInstructions: |
| case UnknownReason::Overflow: |
| case UnknownReason::Trap: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| std::pair<Optional<SILBasicBlock::iterator>, Optional<SymbolicValue>> |
| ConstExprStepEvaluator::tryEvaluateOrElseMakeEffectsNonConstant( |
| SILBasicBlock::iterator instI) { |
| auto evaluateResult = evaluate(instI); |
| Optional<SILBasicBlock::iterator> nextI = evaluateResult.first; |
| Optional<SymbolicValue> errorVal = evaluateResult.second; |
| |
| if (!errorVal) { |
| assert(nextI); |
| return evaluateResult; |
| } |
| assert(!nextI); |
| |
| if (isFailStopError(*errorVal)) { |
| return evaluateResult; |
| } |
| |
| // If evaluation fails on an unconditional branch, it implies there is a loop |
| // at the top level. |
| if (isa<BranchInst>(&(*instI))) { |
| assert(errorVal->getUnknownReason().getKind() == UnknownReason::Loop); |
| return evaluateResult; |
| } |
| |
| // Since the evaluation has failed, make the effects of this instruction |
| // unknown. |
| auto result = skipByMakingEffectsNonConstant(instI); |
| return {result.first, errorVal}; |
| } |
| |
| Optional<SymbolicValue> |
| ConstExprStepEvaluator::lookupConstValue(SILValue value) { |
| auto res = internalState->lookupValue(value); |
| if (res && !res->isConstant()) { |
| return None; |
| } |
| return res; |
| } |
| |
| bool swift::isKnownConstantEvaluableFunction(SILFunction *fun) { |
| return classifyFunction(fun).hasValue(); |
| } |