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

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


The following commit(s) were added to refs/heads/main by this push:
     new 413e24a8b [FIX][RUST] Follow the Ramp integer check and load the LLVM 
backend in tests (#86)
413e24a8b is described below

commit 413e24a8beb6e154e7f3d18412ea8344c71e35e9
Author: Linzhang Li <[email protected]>
AuthorDate: Mon Oct 26 12:28:34 2026 -0400

    [FIX][RUST] Follow the Ramp integer check and load the LLVM backend in 
tests (#86)
    
    Two recent changes on `main` broke four tests of the Rust crate (`cargo
    test --manifest-path
    rust/Cargo.toml`), which CI does not run yet. This PR brings the Rust
    side and the tests back in
    line with them.
    
    - #64 restricts `Ramp` to 32-bit and 64-bit integer bases and strides.
    The Rust `Ramp::new`, which
      follows the C++ constructor, still accepted any scalar, so
    `ir prim::vector_expr::vector_nodes_agree_with_cpp` failed on a float
    base.
    - #72 moves the LLVM code generator into `libtvm_tirx_llvm_backend`,
    which `import tvm_tirx` loads
    and the Rust crate does not, so `codegen.llvm_*` and `target.build.llvm`
    were missing in
    `codegen::llvm::llvm_queries`, `codegen build_module` and `target
    build_by_kind`.
---
 rust/src/ir/prim/op.rs            |  2 +-
 rust/src/ir/prim/vector_expr.rs   | 29 ++++++++++++++++++++++-------
 rust/tests/codegen/llvm.rs        |  3 +++
 rust/tests/codegen/main.rs        |  3 ++-
 rust/tests/common/mod.rs          | 24 ++++++++++++++++++++++--
 rust/tests/ir/prim/op.rs          |  7 +++++--
 rust/tests/ir/prim/vector_expr.rs |  7 ++++++-
 rust/tests/target/main.rs         |  3 ++-
 8 files changed, 63 insertions(+), 15 deletions(-)

diff --git a/rust/src/ir/prim/op.rs b/rust/src/ir/prim/op.rs
index 33f1a8ee8..0aa5f9da2 100644
--- a/rust/src/ir/prim/op.rs
+++ b/rust/src/ir/prim/op.rs
@@ -687,7 +687,7 @@ pub fn cast_at(
         // Casting the base and the stride is valid only when every lane is 
the same integer in
         // both types: widening must not remove an overflow of the source, and 
a floating-point
         // cast need not be affine.
-        let integer = matches!(to.code, support::INT | support::UINT);
+        let integer = matches!(to.code, support::INT | support::UINT) && 
matches!(to.bits, 32 | 64);
         if let (Some(base), Some(stride)) =
             (const_fold::int(&ramp.base_), const_fold::int(&ramp.stride))
         {
diff --git a/rust/src/ir/prim/vector_expr.rs b/rust/src/ir/prim/vector_expr.rs
index 9c44b114e..90f52a5d5 100644
--- a/rust/src/ir/prim/vector_expr.rs
+++ b/rust/src/ir/prim/vector_expr.rs
@@ -266,14 +266,29 @@ impl Ramp {
         loc: impl Into<Location>,
     ) -> Result<Self> {
         let (base, stride, lanes) = (base.into_expr()?, stride.into_expr()?, 
lanes.into_expr()?);
-        let base_dtype = support::prim_dtype(&base)?;
-        let stride_dtype = support::prim_dtype(&stride)?;
+        let base_ty = support::prim_type(&base)?;
+        let stride_ty = support::prim_type(&stride)?;
         support::prim_dtype(&lanes)?;
-        if !support::is_scalar(base_dtype) {
-            return Err(support::check_failed(tvm_ffi::INTERNAL_ERROR, 
"base_ty.IsScalar()", ""));
-        }
-        if !support::is_scalar(stride_dtype) {
-            return Err(support::check_failed(tvm_ffi::INTERNAL_ERROR, 
"stride_ty.IsScalar()", ""));
+        let (base_dtype, stride_dtype) = (base_ty.dtype, stride_ty.dtype);
+        // Only 32-bit and 64-bit integers step through the lanes of a ramp.
+        let index = |dtype: DLDataType| {
+            support::is_scalar(dtype)
+                && matches!(dtype.code, support::INT | support::UINT)
+                && matches!(dtype.bits, 32 | 64)
+        };
+        for (field, ty) in [("base", &base_ty), ("stride", &stride_ty)] {
+            if !index(ty.dtype) {
+                let condition = format!(
+                    "{field}_ty.IsScalar() && \
+                     {field}_ty.MatchesCode(DLDataTypeCode::kDLInt, 
DLDataTypeCode::kDLUInt) && \
+                     ({field}_ty.bits() == 32 || {field}_ty.bits() == 64)"
+                );
+                let message = format!(
+                    "Ramp {field} must be scalar int32, int64, uint32, or 
uint64, but got {}",
+                    support::repr(ty)
+                );
+                return Err(support::check_failed(tvm_ffi::TYPE_ERROR, 
&condition, &message));
+            }
         }
         let stride = match stride_dtype == base_dtype {
             true => stride,
diff --git a/rust/tests/codegen/llvm.rs b/rust/tests/codegen/llvm.rs
index e74843e6f..d2696ccb8 100644
--- a/rust/tests/codegen/llvm.rs
+++ b/rust/tests/codegen/llvm.rs
@@ -22,8 +22,11 @@
 use tvm_tirx::codegen::{self, llvm};
 use tvm_tirx::target::Target;
 
+use crate::common::load_llvm_backend;
+
 #[test]
 fn llvm_queries() {
+    load_llvm_backend();
     assert!(llvm::version_major().unwrap() >= 10);
     let sqrt = llvm::lookup_intrinsic_id("llvm.sqrt").unwrap();
     assert!(sqrt > 0);
diff --git a/rust/tests/codegen/main.rs b/rust/tests/codegen/main.rs
index 86856c24e..7d5389ea5 100644
--- a/rust/tests/codegen/main.rs
+++ b/rust/tests/codegen/main.rs
@@ -28,10 +28,11 @@ mod llvm;
 use tvm_tirx::codegen;
 use tvm_tirx::target::Target;
 
-use common::empty_module;
+use common::{empty_module, load_llvm_backend};
 
 #[test]
 fn build_module() {
+    load_llvm_backend();
     let module = empty_module();
     codegen::build_module(&module, Target::new("llvm").unwrap()).unwrap();
     codegen::build_module(&module, Target::new("c").unwrap()).unwrap();
diff --git a/rust/tests/common/mod.rs b/rust/tests/common/mod.rs
index d4ffe00a5..1a350b0f3 100644
--- a/rust/tests/common/mod.rs
+++ b/rust/tests/common/mod.rs
@@ -21,13 +21,14 @@
 
 #![allow(dead_code)]
 
+use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
 use std::mem::size_of;
-use std::sync::{Mutex, MutexGuard};
+use std::sync::{Mutex, MutexGuard, Once};
 
 use tvm_tirx::ir::{BaseFunc, Expr, GlobalInfo, GlobalVar, IRModule, 
MissingTypeObj, PrimTypeObj};
 use tvm_tirx::tvm_ffi::tvm_ffi_sys::{TVMFFIGetTypeInfo, TVMFFITypeInfo};
 use tvm_tirx::tvm_ffi::{
-    Any, AnyCompatible, AnyView, Array, Function, Map, ObjectCore, 
ObjectRefCore, Result,
+    Any, AnyCompatible, AnyView, Array, Function, Map, Module, ObjectCore, 
ObjectRefCore, Result,
     String as FfiString,
 };
 
@@ -39,6 +40,25 @@ pub fn serial() -> MutexGuard<'static, ()> {
     LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
 }
 
+/// Load the LLVM backend library, which registers the `codegen.llvm_*` 
functions and the `llvm`
+/// target build, as `import tvm_tirx` does. It sits beside 
`libtvm_tirx_compiler`, whose directory
+/// the build script puts on the loader path; a build with 
`TVM_TIRX_LLVM_NATIVE_ONLY` names it
+/// `tvm_tirx_llvm_backend_native`.
+pub fn load_llvm_backend() {
+    static LOAD: Once = Once::new();
+    LOAD.call_once(|| {
+        let mut errors = Vec::new();
+        for name in ["tvm_tirx_llvm_backend", "tvm_tirx_llvm_backend_native"] {
+            match 
Module::load_from_file(format!("{DLL_PREFIX}{name}{DLL_SUFFIX}")) {
+                // Unloading the library would unregister its functions.
+                Ok(backend) => return std::mem::forget(backend),
+                Err(err) => errors.push(err.to_string()),
+            }
+        }
+        panic!("could not load the LLVM backend: {}", errors.join("\n"));
+    });
+}
+
 pub fn global(name: &str) -> Function {
     Function::get_global(name).unwrap()
 }
diff --git a/rust/tests/ir/prim/op.rs b/rust/tests/ir/prim/op.rs
index e7e4c0887..d19061a41 100644
--- a/rust/tests/ir/prim/op.rs
+++ b/rust/tests/ir/prim/op.rs
@@ -769,13 +769,15 @@ fn cast_folds_and_broadcasts_as_cpp() {
         (Expr::from(&x), "int32x4"),
         (Expr::from(&f), "int32x4"),
         (imm("int32", 3), "int64x4"),
-        // A broadcast is cast element by element, and so is a ramp of 
constants cast to an
-        // integer type when every lane fits both types; any other ramp 
becomes a `Cast` node.
+        // A broadcast is cast element by element, and so is a ramp of 
constants cast to a 32-bit
+        // or 64-bit integer type when every lane fits both types; any other 
ramp becomes a `Cast`
+        // node.
         (Broadcast::new(&f, 4, UNKNOWN_LOC).unwrap().into(), "int32x4"),
         (constant.clone(), "int64x4"),
         (overflowing.clone(), "int64x4"),
         (ramp(x.clone().into()), "int64x4"),
         (constant.clone(), "float32x4"),
+        (constant.clone(), "int16x4"),
     ];
     for (value, name) in &cases {
         assert!(same(value.cast(prim(name)), &native_cast(value, name)), 
"{name}");
@@ -790,6 +792,7 @@ fn cast_folds_and_broadcasts_as_cpp() {
     assert!(node(&overflowing, "int64x4").as_node::<CastObj>().is_some());
     assert!(node(&ramp(x.clone().into()), 
"int64x4").as_node::<CastObj>().is_some());
     assert!(node(&constant, "float32x4").as_node::<CastObj>().is_some());
+    assert!(node(&constant, "int16x4").as_node::<CastObj>().is_some());
     let raw: Expr = Cast::new(PrimType::int(64), &x, 
UNKNOWN_LOC).unwrap().into();
     assert!(same(x.cast(PrimType::int(64)), &raw));
 
diff --git a/rust/tests/ir/prim/vector_expr.rs 
b/rust/tests/ir/prim/vector_expr.rs
index 39c7ff452..8ad02a28a 100644
--- a/rust/tests/ir/prim/vector_expr.rs
+++ b/rust/tests/ir/prim/vector_expr.rs
@@ -55,6 +55,8 @@ fn vector_nodes_agree_with_cpp() {
     let _serial = serial();
     let x = Expr::from(var("x", PrimType::int(32)));
     let x64 = Expr::from(var("x64", PrimType::int(64)));
+    let x16 = Expr::from(var("x16", PrimType::int(16)));
+    let u32 = Expr::from(var("u32", PrimType::uint(32)));
     let v4 = Expr::from(var("v", PrimType::int(32).with_lanes(4).unwrap()));
     let bf = Expr::from(var("bf", PrimType::bfloat(32)));
     let f = Expr::from(var("f", PrimType::float(32)));
@@ -82,7 +84,10 @@ fn vector_nodes_agree_with_cpp() {
         ("Ramp", x.clone(), Expr::from(1), lanes_4.clone()),
         ("Ramp(int64 lanes)", x.clone(), Expr::from(1), 
IntImm::int64(4).into()),
         ("Ramp(int64 stride constant)", x.clone(), IntImm::int64(2).into(), 
lanes_4.clone()),
-        ("Ramp(float stride constant)", f.clone(), Expr::from(2), 
lanes_4.clone()),
+        ("Ramp(uint32 base)", u32.clone(), Expr::from(1), lanes_4.clone()),
+        ("Ramp(int16 base)", x16.clone(), Expr::from(1), lanes_4.clone()),
+        ("Ramp(int16 stride)", x.clone(), x16.clone(), lanes_4.clone()),
+        ("Ramp(float base)", f.clone(), Expr::from(2), lanes_4.clone()),
         ("Ramp(int32 to int64 stride constant)", x64.clone(), Expr::from(-3), 
lanes_4.clone()),
         ("Ramp(int64 stride)", x.clone(), x64.clone(), lanes_4.clone()),
         ("Ramp(int stride of float base)", f.clone(), x.clone(), 
lanes_4.clone()),
diff --git a/rust/tests/target/main.rs b/rust/tests/target/main.rs
index d1ca39ec6..d7c62763f 100644
--- a/rust/tests/target/main.rs
+++ b/rust/tests/target/main.rs
@@ -26,7 +26,7 @@ mod common;
 use tvm_tirx::target::{self, Target, TargetKind, TargetKindObj, TargetObj};
 use tvm_tirx::tvm_ffi::{Any, Map, Object, ObjectRefCast, String as FfiString, 
TypeIndex};
 
-use common::{assert_type_contracts, empty_module, global};
+use common::{assert_type_contracts, empty_module, global, load_llvm_backend};
 
 fn config(kind: &str) -> Map<FfiString, Any> {
     Map::from_iter([(FfiString::from("kind"), 
Any::from(FfiString::from(kind)))])
@@ -122,6 +122,7 @@ fn tags() {
 
 #[test]
 fn build_by_kind() {
+    load_llvm_backend();
     let c = Target::new("c").unwrap();
     target::build("c", empty_module(), &c).unwrap();
     target::build("llvm", empty_module(), 
Target::new("llvm").unwrap()).unwrap();

Reply via email to