This is an automated email from the ASF dual-hosted git repository.

MasterJH5574 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new 0cc110ecd2 [CI] Bump CI at the Ubuntu 24.04 images and re-enable 
USE_Z3 (#19911)
0cc110ecd2 is described below

commit 0cc110ecd25f1befaa928dd5935f1d64663c6974
Author: Shushi Hong <[email protected]>
AuthorDate: Tue Jul 14 17:27:00 2026 -0400

    [CI] Bump CI at the Ubuntu 24.04 images and re-enable USE_Z3 (#19911)
    
    This pr switches CI to the Ubuntu 24.04 (noble) images. Bump ci_tag in
    ci/jenkins/docker-images.ini to 20260629-192919-24bbfd2e -- the images
    built from #19893 (ci_cpu/ci_arm/ci_wasm/ci_gpu on Ubuntu 24.04, whose
    default g++ is gcc-13, giving full C++20 support).
    
    Also, this pr re-enables Z3 (AUTO). #19828 temporarily set USE_Z3=OFF
    (in CMakeLists.txt and the pyproject wheel build) to dodge a z3-static
    build failure. The CI image now ships z3-static (#19835), so this
    restores USE_Z3=AUTO: the Z3-backed Analyzer proving is enabled when
    z3-static is available and silently skipped otherwise.
    
    While Z3 stayed disabled, PrimExprNode::ty became a method, leaving two
    stale field accesses in z3_prover.cc's IsZ3SupportedExpr (only compiled
    under TVM_USE_Z3). Fixed expr->ty -> expr->ty().
    
    Verification:
    - The Ubuntu 24.04 images (#19893) built successfully for ci_cpu/ci_arm/
    ci_wasm/ci_gpu (the GPU image includes ROCm 6.4.4 and the CUDA 24.04
    base).
    - Re-enabling Z3 was validated with a build-only wheel run: all four
    wheels (Linux x86_64/aarch64 manylinux_2_28, macOS arm64, Windows) build
    green with z3-static compiled and linked, confirming the earlier
    z3-static link concern is resolved on the current toolchain.
---
 CMakeLists.txt                      |  2 +-
 ci/jenkins/docker-images.ini        |  2 +-
 pyproject.toml                      |  6 +++---
 src/arith/z3_prover.cc              | 27 +++++++++++++++------------
 tests/python/arith/test_arith_z3.py |  6 +++---
 5 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3d183253b1..567edc1dc6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -89,7 +89,7 @@ tvm_option(COMPILER_RT_PATH "Path to COMPILER-RT" 
"3rdparty/compiler-rt")
 # Contrib library options
 tvm_option(USE_BLAS "The blas library to be linked" none)
 tvm_option(USE_AMX "Enable Intel AMX" OFF)
-tvm_option(USE_Z3 "Build with Z3 SMT solver support" OFF)
+tvm_option(USE_Z3 "Build with Z3 SMT solver support" AUTO)
 tvm_option(USE_MKL "MKL root path when use MKL blas" OFF)
 tvm_option(USE_DNNL "Enable DNNL codegen" OFF)
 tvm_option(USE_CUDNN "Build with cuDNN" OFF)
diff --git a/ci/jenkins/docker-images.ini b/ci/jenkins/docker-images.ini
index 175ea8dded..8576f238c6 100644
--- a/ci/jenkins/docker-images.ini
+++ b/ci/jenkins/docker-images.ini
@@ -17,7 +17,7 @@
 
 # This data file is read during when Jenkins runs job to determine docker 
images.
 [jenkins]
-ci_tag: 20260619-214849-4174cdf5
+ci_tag: 20260705-142349-cfb98e93
 ci_arm: tlcpack/ci-arm:%(ci_tag)s
 ci_cpu: tlcpack/ci-cpu:%(ci_tag)s
 ci_gpu: tlcpack/ci-gpu:%(ci_tag)s
diff --git a/pyproject.toml b/pyproject.toml
index 810a86349f..f7cb27d778 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -16,7 +16,7 @@
 # under the License.
 
 [build-system]
-# z3-static ships the PIC static libz3 + headers for explicit USE_Z3=ON builds.
+# z3-static ships the PIC static libz3 + headers consumed by USE_Z3=ON.
 requires = [
   "scikit-build-core>=0.11",
   "setuptools-scm>=8",
@@ -146,8 +146,8 @@ logging.level = "INFO"
 [tool.scikit-build.cmake.define]
 TVM_BUILD_PYTHON_MODULE = "ON"
 USE_CUDA = "OFF"
-# Keep Z3 disabled by default until CI's C++ toolchain can link z3-static 
reliably.
-USE_Z3 = "OFF"
+# Statically link Z3 from the z3-static build dependency by default.
+USE_Z3 = "AUTO"
 BUILD_TESTING = "OFF"
 
 [tool.setuptools_scm]
diff --git a/src/arith/z3_prover.cc b/src/arith/z3_prover.cc
index 7677d8ffe4..e608c70e8a 100644
--- a/src/arith/z3_prover.cc
+++ b/src/arith/z3_prover.cc
@@ -267,7 +267,7 @@ class Z3Prover::Impl : ExprFunctor<z3::expr(const Expr&)> {
     scope_stack_.back().push_back(Scope{Scope::BindValue, var, value});
     // we add the binding whenever the value is pure,
     // because non-pure parts are handling by creating free variables in 
VisitExpr
-    memo_.emplace(var, ConvertInt(value));
+    memo_.emplace(var.as_or_throw<PrimExpr>(), ConvertInt(value));
   }
 
   /// @brief Bind a variable to a range
@@ -279,7 +279,7 @@ class Z3Prover::Impl : ExprFunctor<z3::expr(const Expr&)> {
     //    if the var is overrided later, we can just update the memo, and the 
old placeholder will
     //    be ignored
     auto var_expr = Create(var.get());
-    memo_.emplace(var, var_expr);
+    memo_.emplace(var.as_or_throw<PrimExpr>(), var_expr);
 
     // 2. Add constraint on the placeholder
     //    when min_expr >= max_expr, the range is empty, which is under 
undefined behavior
@@ -299,8 +299,9 @@ class Z3Prover::Impl : ExprFunctor<z3::expr(const Expr&)> {
         solver.add(var_expr < ctx->int_val(max_value));
       }
     } else {
+      PrimExpr prim_var = var.as_or_throw<PrimExpr>();
       solver.add(ConvertBool(range->extent <= 0 ||
-                             (range->min <= var && var < range->min + 
range->extent)));
+                             (range->min <= prim_var && prim_var < range->min 
+ range->extent)));
     }
   }
 
@@ -435,7 +436,7 @@ class Z3Prover::Impl : ExprFunctor<z3::expr(const Expr&)> {
     solver.push();
 
     // Convert the TVM variable to Z3 expression
-    z3::expr z3_var = VisitInt(var);
+    z3::expr z3_var = VisitInt(var.as_or_throw<PrimExpr>());
 
     int64_t count = 0;
     std::vector<int64_t> found_values;
@@ -596,7 +597,7 @@ class Z3Prover::Impl : ExprFunctor<z3::expr(const Expr&)> {
 
   z3::expr VisitExpr_(const LetNode* op) override {
     if (IsZ3SupportedExpr(op->var.get())) {
-      memo_.emplace(op->var, VisitInt(op->value));
+      memo_.emplace(op->var.as_or_throw<PrimExpr>(), VisitInt(op->value));
     }
     return VisitExpr(op->body);
   }
@@ -704,7 +705,9 @@ class Z3Prover::Impl : ExprFunctor<z3::expr(const Expr&)> {
     } else if (op->op.same_as(tirx::builtin::if_then_else()) && 
op->args.size() == 3 &&
                IsZ3SupportedExpr(op->args[1].get()) && 
IsZ3SupportedExpr(op->args[2].get())) {
       // tir.if_then_else(cond, a, b) is a select-like ternary.
-      return z3::ite(VisitBool(op->args[0]), VisitInt(op->args[1]), 
VisitInt(op->args[2]));
+      return z3::ite(VisitBool(op->args[0].as_or_throw<PrimExpr>()),
+                     VisitInt(op->args[1].as_or_throw<PrimExpr>()),
+                     VisitInt(op->args[2].as_or_throw<PrimExpr>()));
     } else {
       // For other call nodes, create a free variable
       return Create(op);
@@ -719,9 +722,9 @@ class Z3Prover::Impl : ExprFunctor<z3::expr(const Expr&)> {
       TVM_FFI_UNREACHABLE();
     }
 
-    const PrimExpr& a = op->args[0];
-    const PrimExpr& b = op->args[1];
-    unsigned bit_width = std::max(op->args[0].ty().bits(), 
op->args[1].ty().bits());
+    PrimExpr a = op->args[0].as_or_throw<PrimExpr>();
+    PrimExpr b = op->args[1].as_or_throw<PrimExpr>();
+    unsigned bit_width = std::max(a.ty().bits(), b.ty().bits());
 
     if (IsZ3SupportedExpr(a.get()) && IsZ3SupportedExpr(b.get())) {
       return z3::bv2int(
@@ -738,7 +741,7 @@ class Z3Prover::Impl : ExprFunctor<z3::expr(const Expr&)> {
       TVM_FFI_UNREACHABLE();
     }
 
-    const PrimExpr& a = op->args[0];
+    PrimExpr a = op->args[0].as_or_throw<PrimExpr>();
 
     if (IsZ3SupportedExpr(a.get())) {
       // Cast integer to bit-vector, apply bitwise not, then cast back.
@@ -758,8 +761,8 @@ class Z3Prover::Impl : ExprFunctor<z3::expr(const Expr&)> {
       TVM_FFI_UNREACHABLE();
     }
 
-    const PrimExpr& a = op->args[0];
-    const PrimExpr& b = op->args[1];
+    PrimExpr a = op->args[0].as_or_throw<PrimExpr>();
+    PrimExpr b = op->args[1].as_or_throw<PrimExpr>();
 
     // Shift operations require integer types for both operands
     if (IsZ3SupportedExpr(a.get()) && IsZ3SupportedExpr(b.get())) {
diff --git a/tests/python/arith/test_arith_z3.py 
b/tests/python/arith/test_arith_z3.py
index a64afd76c5..a06cd9ca62 100644
--- a/tests/python/arith/test_arith_z3.py
+++ b/tests/python/arith/test_arith_z3.py
@@ -96,9 +96,10 @@ def test_z3_context_lifetime_outlives_worker_thread():
 # ---------------------------------------------------------------------------
 # Examples the native analyzer cannot prove but Z3 can.
 #
-# Each case asserts both that the native analyzers (kDefault, Z3 gated off)
+# Most cases assert both that the native analyzers (kDefault, Z3 gated off)
 # fail and that Z3 (kSymbolicBound) succeeds. This demonstrates the added value
-# of the Z3 backend and that it is correctly gated behind kSymbolicBound.
+# of the Z3 backend and that it is correctly gated behind kSymbolicBound. Cases
+# the native analyzer learns to prove remain here as Z3 translation 
regressions.
 # ---------------------------------------------------------------------------
 
 
@@ -155,7 +156,6 @@ def test_z3_nested_floor_division_collapse():
         tirx.all(a >= 0, a < 128),
         a // 128 == (a // 64 * 32 + a % 32 // 16 * 8) // 64,
     )
-    assert not analyzer.can_prove(expr)
     assert analyzer.can_prove(expr, SB)
 
 

Reply via email to