Program Listing for File analyzer.h#
↰ Return to documentation for file (tvm/sym/analyzer.h)
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef TVM_SYM_ANALYZER_H_
#define TVM_SYM_ANALYZER_H_
#include <tvm/ffi/cast.h>
#include <tvm/ffi/object.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ffi/string.h>
#include <tvm/ir/expr.h>
#include <tvm/ir/with_context.h>
#include <tvm/sym/constraint.h>
#include <tvm/sym/int_set.h>
#include <functional>
#include <limits>
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
namespace tvm {
namespace sym {
//-------------------------------------------------------
// Base integer analysis API.
//
// We have multiple type of analyzers to do relaxed
// integer set analysis(bound analysis, modulo) and
// equivalence checking and simplification.
//
// Importantly, each analyzer may need result from
// another analyzer.
//-------------------------------------------------------
// Forward declare the analyzer object and its reference handle.
class AnalyzerObj;
class Analyzer;
enum class CompareResult : int;
namespace detail {
class TempConstraintScope;
} // namespace detail
enum DivMode {
kTruncDiv,
kFloorDiv
};
enum class ProofStrength : int {
kDefault = 0,
kSymbolicBound = 1,
};
class ConstIntBoundObj : public ffi::Object {
public:
int64_t min_value;
int64_t max_value;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<ConstIntBoundObj>()
.def_ro("min_value", &ConstIntBoundObj::min_value)
.def_ro("max_value", &ConstIntBoundObj::max_value);
}
static const constexpr int64_t kPosInf = std::numeric_limits<int64_t>::max();
static const constexpr int64_t kNegInf = -kPosInf;
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("sym.ConstIntBound", ConstIntBoundObj, ffi::Object);
};
class ConstIntBound : public ffi::ObjectRef {
public:
TVM_TIRX_DLL ConstIntBound(int64_t min_value, int64_t max_value);
static const constexpr int64_t kPosInf = ConstIntBoundObj::kPosInf;
static const constexpr int64_t kNegInf = ConstIntBoundObj::kNegInf;
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(ConstIntBound, ffi::ObjectRef, ConstIntBoundObj);
};
class ConstIntBoundAnalyzer {
public:
using BoundMapType =
std::unordered_map<PrimExpr, ConstIntBound, ffi::ObjectPtrHash, ffi::ObjectPtrEqual>;
TVM_TIRX_DLL ConstIntBound operator()(const PrimExpr& expr) const;
TVM_TIRX_DLL ConstIntBound operator()(const PrimExpr& expr, BoundMapType* bound);
TVM_TIRX_DLL bool IsBound(const Var& var) const;
private:
friend class AnalyzerObj;
// Clear context-derived facts before replay; retain query settings and diagnostics.
void Reset();
// Record a value binding after AnalyzerObj validates and orders the premises.
void IngestBinding(const Var& var, const PrimExpr& expr);
// Record a half-open variable range from the accepted premise prefix.
void IngestBinding(const Var& var, const Range& range);
// Consume a predicate prepared against the preceding premise prefix.
void IngestPredicate(const PrimExpr& constraint);
// Borrow the owning analyzer for cross-backend queries.
explicit ConstIntBoundAnalyzer(AnalyzerObj* parent);
// Exported for destruction through the inline Analyzer object allocator.
TVM_TIRX_DLL ~ConstIntBoundAnalyzer();
// Copy durable facts/settings into this backend without sharing mutable query state.
void CopyFrom(const ConstIntBoundAnalyzer& other);
struct Entry;
class Impl;
// Owned backend state; its definition stays inside the compiler library.
std::unique_ptr<Impl> impl_;
};
class ModularSetObj : public ffi::Object {
public:
int64_t coeff;
int64_t base;
static void RegisterReflection() {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<ModularSetObj>()
.def_ro("coeff", &ModularSetObj::coeff)
.def_ro("base", &ModularSetObj::base);
}
static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind = kTVMFFISEqHashKindTreeNode;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("sym.ModularSet", ModularSetObj, ffi::Object);
};
class ModularSet : public ffi::ObjectRef {
public:
TVM_TIRX_DLL ModularSet(int64_t coeff, int64_t base);
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(ModularSet, ffi::ObjectRef, ModularSetObj);
};
class ModularSetAnalyzer {
public:
TVM_TIRX_DLL ModularSet operator()(const PrimExpr& expr);
private:
friend class AnalyzerObj;
// Clear context-derived facts before replay; retain query settings and diagnostics.
void Reset();
// Record a value binding after AnalyzerObj validates and orders the premises.
void IngestBinding(const Var& var, const PrimExpr& expr);
// Consume a predicate prepared against the preceding premise prefix.
void IngestPredicate(const PrimExpr& constraint);
// Borrow the owning analyzer for cross-backend queries.
explicit ModularSetAnalyzer(AnalyzerObj* parent);
// Exported for destruction through the inline Analyzer object allocator.
TVM_TIRX_DLL ~ModularSetAnalyzer();
// Copy durable facts/settings into this backend without sharing mutable query state.
void CopyFrom(const ModularSetAnalyzer& other);
struct Entry;
class Impl;
// Owned backend state; its definition stays inside the compiler library.
std::unique_ptr<Impl> impl_;
};
class RewriteSimplifier {
public:
TVM_TIRX_DLL PrimExpr operator()(const PrimExpr& expr);
TVM_TIRX_DLL CompareResult TryCompare(const PrimExpr& lhs, const PrimExpr& rhs);
TVM_TIRX_DLL ffi::ObjectRef GetStatsCounters() const;
TVM_TIRX_DLL void ResetStatsCounters();
TVM_TIRX_DLL void SetMaximumRewriteSteps(int64_t maximum);
private:
friend class AnalyzerObj;
// Clear context-derived facts before replay; retain query settings and diagnostics.
void Reset();
// Keep the original relation alongside the simplified substitution.
void IngestBinding(const Var& var, const PrimExpr& value, const PrimExpr& original);
// Record a range relation after AnalyzerObj validates and orders the premises.
void IngestBinding(const Var& var, const Range& range);
// Consume a predicate prepared against the preceding premise prefix.
void IngestPredicate(const PrimExpr& constraint);
friend class CanonicalSimplifier;
// Borrow the owning analyzer for cross-backend queries.
explicit RewriteSimplifier(AnalyzerObj* parent);
// Exported for destruction through the inline Analyzer object allocator.
TVM_TIRX_DLL ~RewriteSimplifier();
// Copy durable facts/settings into this backend without sharing mutable query state.
void CopyFrom(const RewriteSimplifier& other);
class Impl;
// Owned backend state; its definition stays inside the compiler library.
ffi::ObjectPtr<Impl> impl_;
};
class CanonicalSimplifier {
public:
TVM_TIRX_DLL PrimExpr operator()(const PrimExpr& expr);
private:
friend class AnalyzerObj;
// Clear context-derived facts before replay; retain query settings and diagnostics.
void Reset();
// Record a value binding after AnalyzerObj validates and orders the premises.
void IngestBinding(const Var& var, const PrimExpr& expr);
// Borrow the owning analyzer for cross-backend queries.
explicit CanonicalSimplifier(AnalyzerObj* parent);
// Exported for destruction through the inline Analyzer object allocator.
TVM_TIRX_DLL ~CanonicalSimplifier();
// Copy durable facts/settings into this backend without sharing mutable query state.
void CopyFrom(const CanonicalSimplifier& other);
class Impl;
// Owned backend state; its definition stays inside the compiler library.
ffi::ObjectPtr<Impl> impl_;
};
enum class CompareResult : int {
kInconsistent = 0,
kEQ = 1,
kLT = 2,
kLE = 3,
kGT = 4,
kGE = 5,
kNE = 6,
kUnknown = 7
};
inline constexpr CompareResult operator&(CompareResult lhs, CompareResult rhs) {
return CompareResult(static_cast<int>(lhs) & static_cast<int>(rhs));
}
inline constexpr CompareResult operator|(CompareResult lhs, CompareResult rhs) {
return CompareResult(static_cast<int>(lhs) | static_cast<int>(rhs));
}
class IntSetAnalyzer {
public:
TVM_TIRX_DLL IntSet operator()(const PrimExpr& expr, const ffi::Map<Var, IntSet>& dom_map);
TVM_TIRX_DLL IntSet operator()(const PrimExpr& expr);
private:
friend class AnalyzerObj;
// Clear context-derived facts before replay; retain query settings and diagnostics.
void Reset();
// Record a value binding after AnalyzerObj validates and orders the premises.
void IngestBinding(const Var& var, const PrimExpr& expr);
// Record a half-open variable range from the accepted premise prefix.
void IngestBinding(const Var& var, const Range& range);
// Consume a predicate prepared against the preceding premise prefix.
void IngestPredicate(const PrimExpr& constraint);
// Borrow the owning analyzer for cross-backend queries.
explicit IntSetAnalyzer(AnalyzerObj* parent);
// Exported for destruction through the inline Analyzer object allocator.
TVM_TIRX_DLL ~IntSetAnalyzer();
// Copy durable facts/settings into this backend without sharing mutable query state.
void CopyFrom(const IntSetAnalyzer& other);
class Impl;
// Owned backend state; its definition stays inside the compiler library.
Impl* impl_;
};
class Z3Prover {
public:
TVM_TIRX_DLL bool IsEnabled() const;
TVM_TIRX_DLL bool CanProve(const PrimExpr& expr);
TVM_TIRX_DLL ffi::String GetSMTLIB2(const ffi::Optional<PrimExpr> expr);
TVM_TIRX_DLL ffi::String GetStats();
TVM_TIRX_DLL void SetTimeoutMs(unsigned timeout_ms);
TVM_TIRX_DLL void SetRLimit(unsigned rlimit);
TVM_TIRX_DLL ffi::String GetModel(const PrimExpr& expr);
TVM_TIRX_DLL int64_t CountSatisfyingValues(const Var& var, int64_t max_count = 2048,
int64_t min_consecutive = 1);
private:
friend class AnalyzerObj;
// Clear facts, translations, and solver state; retain resource limits.
void Reset();
// Record a value binding after AnalyzerObj validates and orders the premises.
void IngestBinding(const Var& var, const PrimExpr& expr);
// Record a half-open variable range from the accepted premise prefix.
void IngestBinding(const Var& var, const Range& range);
// Consume a predicate prepared against the preceding premise prefix.
void IngestPredicate(const PrimExpr& constraint);
friend class Analyzer;
// Borrow the owning analyzer for cross-backend queries.
explicit Z3Prover(AnalyzerObj* parent);
// Exported for destruction through the inline Analyzer object allocator.
TVM_TIRX_DLL ~Z3Prover();
// Copy durable facts/settings into this backend without sharing mutable query state.
void CopyFrom(const Z3Prover& other);
class Impl;
std::unique_ptr<Impl> impl_;
};
class TVM_TIRX_DLL AnalyzerObj : public ffi::Object {
public:
ConstIntBoundAnalyzer const_int_bound;
ModularSetAnalyzer modular_set;
RewriteSimplifier rewrite_simplify;
CanonicalSimplifier canonical_simplify;
IntSetAnalyzer int_set;
Z3Prover z3_prover;
AnalyzerObj();
void SetZ3DefaultFallback(bool enabled);
void SyncConstraintContext(const ConstraintContext& context);
ConstraintContext GetConstraintContext() const;
bool CanProveGreaterEqual(const PrimExpr& expr, int64_t lower_bound);
bool CanProveLess(const PrimExpr& expr, int64_t upper_bound);
bool CanProveEqual(const PrimExpr& lhs, const PrimExpr& rhs);
bool CanProveLessEqualThanSymbolicShapeValue(const PrimExpr& lhs, const PrimExpr& shape);
bool CanProve(const PrimExpr& cond, ProofStrength strength = ProofStrength::kDefault);
PrimExpr Simplify(const PrimExpr& expr, int steps = 2);
Analyzer Clone() const;
static constexpr bool _type_mutable = true;
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("sym.Analyzer", AnalyzerObj, ffi::Object);
private:
friend class detail::TempConstraintScope;
// Policy for DEFAULT-strength queries; explicit stronger queries can still use Z3.
bool z3_default_fallback_{true};
// Record a value binding after AnalyzerObj validates and orders the premises.
void IngestBinding(const Var& var, const PrimExpr& expr);
// Record a half-open variable range from the accepted premise prefix.
void IngestBinding(const Var& var, const Range& range);
// Dispatch a supported pure premise to the participating backends.
void IngestConstraint(const Constraint& constraint);
// Normalize and derive predicate facts using only the already valid prefix.
std::vector<PrimExpr> PreparePredicate(const PrimExpr& condition);
// Accepted premise prefix, advanced after each fact during synchronization.
ConstraintContext last_snapshot_;
// Query-local premises, managed in LIFO order by TempConstraintScope.
std::vector<Constraint> temp_constraints_;
// Prevent reentry and reuse after an incomplete synchronization.
bool synchronizing_{false};
// Nested query count; synchronization requires zero active queries.
int active_queries_{0};
};
class Analyzer : public ffi::ObjectRef {
public:
Analyzer() : Analyzer(ffi::make_object<AnalyzerObj>()) {}
explicit Analyzer(ffi::ObjectPtr<AnalyzerObj> n) : ffi::ObjectRef(std::move(n)) {
TVM_FFI_ICHECK(this->get() != nullptr);
}
TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(Analyzer, ffi::ObjectRef, AnalyzerObj);
};
class AllowUIntAsIndex {
public:
TVM_TIRX_DLL static bool Enabled();
private:
friend class With<AllowUIntAsIndex>;
// Called across the DLL boundary by the inline With<> constructor.
TVM_TIRX_DLL void EnterWithScope();
// Called across the DLL boundary by the inline With<> destructor.
TVM_TIRX_DLL void ExitWithScope();
// Number of active scopes in this thread.
static thread_local int depth_;
};
} // namespace sym
} // namespace tvm
#endif // TVM_SYM_ANALYZER_H_