From 09c57c170207d1ef0682a30b787906002ae3c71b Mon Sep 17 00:00:00 2001 From: haixuantao Date: Mon, 8 Jun 2026 14:54:49 +0200 Subject: [PATCH 1/5] fix(mir-lower): fall back to live operand type in arithmetic signedness check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `is_signed_int_op` errored ("expected IntegerType or MirPtrType operand") when the pre-conversion MIR type was not tracked for an operand — which happens for booleans and values produced inside short-circuit `||`/`&&`, or already-converted operands. Fall back to the live operand type: a (signless, post-conversion) integer or a pointer is treated as unsigned, instead of failing the whole lowering. --- crates/mir-lower/src/convert/ops/arithmetic.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/mir-lower/src/convert/ops/arithmetic.rs b/crates/mir-lower/src/convert/ops/arithmetic.rs index a30d78f9b6..e9fe83700d 100644 --- a/crates/mir-lower/src/convert/ops/arithmetic.rs +++ b/crates/mir-lower/src/convert/ops/arithmetic.rs @@ -87,10 +87,20 @@ fn is_signed_int_op( { Ok(false) } else { - pliron::input_err!( - op.deref(ctx).loc(), - "expected IntegerType or MirPtrType operand in arithmetic op" - ) + // Pre-conversion MIR type wasn't tracked for this operand (can happen + // for booleans / values produced inside short-circuit `||`/`&&` or + // already-converted operands). Fall back to the live operand type: an + // integer (signless post-conversion) or pointer is treated as unsigned. + let live = operand.get_type(ctx); + let live_ref = live.deref(ctx); + if live_ref.is::() || live_ref.is::() { + Ok(false) + } else { + pliron::input_err!( + op.deref(ctx).loc(), + "expected IntegerType or MirPtrType operand in arithmetic op" + ) + } } } From 4f06905f3ad975dcbe26aec6d38ca162c3509b80 Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Fri, 12 Jun 2026 12:06:52 +0530 Subject: [PATCH 2/5] fix(mir-lower): honor live IntegerType signedness in untracked fallback The fallback added for untracked operands treated any live integer as unsigned. That is correct today because the only values without conversion history are signless i1 bools (signless is not Signed), but a blanket "unsigned" would silently miscompile if a future pass ever produced an untracked Signed-typed operand. Consult the live type's signedness instead, and correct the comment: replace_operation records history for already-converted operands, so the untracked case is exactly values whose type was never convertible. --- crates/mir-lower/src/convert/ops/arithmetic.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/mir-lower/src/convert/ops/arithmetic.rs b/crates/mir-lower/src/convert/ops/arithmetic.rs index c51d3cf378..d988150e7d 100644 --- a/crates/mir-lower/src/convert/ops/arithmetic.rs +++ b/crates/mir-lower/src/convert/ops/arithmetic.rs @@ -72,7 +72,9 @@ fn is_float_type(ctx: &Context, val: Value) -> bool { /// /// Uses `operands_info` to read the *pre-conversion* MIR type (which preserves /// Rust's signedness). After DialectConversion, the live operand type is already -/// signless. Pointer types are treated as unsigned. +/// signless. Pointer types are treated as unsigned. Operands with no conversion +/// history (types that were never convertible, today only signless i1 bools) +/// fall back to the live type's signedness. fn is_signed_int_op( ctx: &Context, op: Ptr, @@ -87,13 +89,17 @@ fn is_signed_int_op( { Ok(false) } else { - // Pre-conversion MIR type wasn't tracked for this operand (can happen - // for booleans / values produced inside short-circuit `||`/`&&` or - // already-converted operands). Fall back to the live operand type: an - // integer (signless post-conversion) or pointer is treated as unsigned. + // No conversion history exists for this operand: its type was never + // convertible, so DialectConversion recorded nothing for it. Today + // that is only signless i1 bools (e.g. a phi produced by + // short-circuit `||`/`&&`). Fall back to the live operand type and + // honor its signedness: signless lowers as unsigned, and pointers + // are always unsigned. let live = operand.get_type(ctx); let live_ref = live.deref(ctx); - if live_ref.is::() || live_ref.is::() { + if let Some(int_ty) = live_ref.downcast_ref::() { + Ok(int_ty.signedness() == Signedness::Signed) + } else if live_ref.is::() { Ok(false) } else { pliron::input_err!( From 81a31601f142294547991775afb956cc3ffc31e0 Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Fri, 12 Jun 2026 12:11:15 +0530 Subject: [PATCH 3/5] test(mir-lower): lock i1 bool-phi comparisons to unsigned icmp Builds the short-circuit shape (let p = a || b; p == q; p < q) directly in dialect-mir: a merge-block i1 argument has no conversion history because signless i1 is rejected by can_convert_type, so the comparison must go through the live-type fallback in is_signed_int_op. Asserts the lowered body contains exactly icmp eq i1 and icmp ult i1. Reverting the fallback reproduces the original failure (expected IntegerType or MirPtrType operand in arithmetic op). --- crates/mir-lower/tests/lowering_test.rs | 158 ++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/crates/mir-lower/tests/lowering_test.rs b/crates/mir-lower/tests/lowering_test.rs index 9e83bca842..26e03773c4 100644 --- a/crates/mir-lower/tests/lowering_test.rs +++ b/crates/mir-lower/tests/lowering_test.rs @@ -713,3 +713,161 @@ fn test_cmp_predicate_lowering() -> Result<(), anyhow::Error> { ); Ok(()) } + +/// Regression cover for PR #141: comparisons whose operand is a bool phi. +/// +/// Bools are signless i1, which `can_convert_type` rejects (signless is +/// already the LLVM form), so DialectConversion records no type history for +/// a bool block argument. `is_signed_int_op` used to error out for such +/// operands ("expected IntegerType or MirPtrType operand in arithmetic op"); +/// it must instead fall back to the live operand type and lower the +/// comparison as unsigned. +/// +/// The function mirrors the MIR of a short-circuit kernel: +/// +/// ```text +/// let p = a || b; // bool phi: merge block argument +/// out = (p == q, p < q); // icmp eq i1 / icmp ult i1 +/// ``` +/// +/// ```text +/// bb0(a: i1, b: i1, q: i1): mir.cond_br a, bb2(a), bb1() +/// bb1(): mir.goto bb2(b) +/// bb2(p: i1): mir.eq p, q ; mir.lt p, q ; mir.return +/// ``` +#[test] +fn test_bool_phi_cmp_lowers_to_unsigned_i1_icmp() -> Result<(), anyhow::Error> { + use llvm_export::attributes::ICmpPredicateAttr; + use pliron::basic_block::BasicBlock; + use pliron::builtin::op_interfaces::OperandSegmentInterface; + use pliron::builtin::types::{FunctionType, IntegerType, Signedness}; + use pliron::r#type::Typed; + + let mut ctx = Context::new(); + dialect_mir::register(&mut ctx); + dialect_nvvm::register(&mut ctx); + mir_lower::register(&mut ctx); + + let module = ModuleOp::new(&mut ctx, "test_module".try_into().unwrap()); + let module_ptr = module.get_operation(); + + let bool_ty = IntegerType::get(&mut ctx, 1, Signedness::Signless); + let arg_tys: Vec> = + vec![bool_ty.into(), bool_ty.into(), bool_ty.into()]; + let func_name = "bool_phi_cmp"; + let func_ty = FunctionType::get(&mut ctx, arg_tys.clone(), vec![]); + + let func_op_ptr = Operation::new( + &mut ctx, + mir::MirFuncOp::get_concrete_op_info(), + vec![], + vec![], + vec![], + 1, + ); + let func_ty_attr = pliron::builtin::attributes::TypeAttr::new(func_ty.into()); + let func = mir::MirFuncOp::new(&mut ctx, func_op_ptr, func_ty_attr); + func.set_symbol_name(&mut ctx, func_name.try_into().unwrap()); + + let region = func.get_operation().deref(&ctx).get_region(0); + + // bb0(a, b, q): the function entry. + let bb0 = BasicBlock::new(&mut ctx, None, arg_tys); + bb0.insert_at_back(region, &ctx); + let a = bb0.deref(&ctx).get_argument(0); + let b = bb0.deref(&ctx).get_argument(1); + let q = bb0.deref(&ctx).get_argument(2); + + // bb1(): the short-circuit "evaluate b" block. + let bb1 = BasicBlock::new(&mut ctx, None, vec![]); + bb1.insert_at_back(region, &ctx); + + // bb2(p): the merge block; `p` is the bool phi. + let bb2 = BasicBlock::new(&mut ctx, None, vec![bool_ty.into()]); + bb2.insert_at_back(region, &ctx); + let p = bb2.deref(&ctx).get_argument(0); + + // bb0: cond_br a, bb2(a), bb1(). On the true edge `a` is true, so + // passing `a` itself is `a || b` without needing a constant. + let (flat_operands, segment_sizes) = + mir::MirCondBranchOp::compute_segment_sizes(vec![vec![a], vec![a], vec![]]); + let cond_br = Operation::new( + &mut ctx, + mir::MirCondBranchOp::get_concrete_op_info(), + vec![], + flat_operands, + vec![bb2, bb1], + 0, + ); + Operation::get_op::(cond_br, &ctx) + .expect("MirCondBranchOp") + .set_operand_segment_sizes(&mut ctx, segment_sizes); + cond_br.insert_at_back(bb0, &ctx); + + // bb1: goto bb2(b). + let goto = Operation::new( + &mut ctx, + mir::MirGotoOp::get_concrete_op_info(), + vec![], + vec![b], + vec![bb2], + 0, + ); + goto.insert_at_back(bb1, &ctx); + + // bb2: p == q, then p < q. + for info in [ + mir::MirEqOp::get_concrete_op_info(), + mir::MirLtOp::get_concrete_op_info(), + ] { + let cmp = Operation::new(&mut ctx, info, vec![bool_ty.into()], vec![p, q], vec![], 0); + cmp.insert_at_back(bb2, &ctx); + } + let ret_op = Operation::new( + &mut ctx, + mir::MirReturnOp::get_concrete_op_info(), + vec![], + vec![], + vec![], + 0, + ); + ret_op.insert_at_back(bb2, &ctx); + + let module_region = module.get_operation().deref(&ctx).get_region(0); + let module_block = module_region.deref(&ctx).iter(&ctx).next().unwrap(); + func.get_operation().insert_at_back(module_block, &ctx); + + // Before the fallback, this failed with "expected IntegerType or + // MirPtrType operand in arithmetic op". + mir_lower::lower_mir_to_llvm(&mut ctx, module_ptr).map_err(|e| anyhow::anyhow!("{}", e))?; + + let mut icmps = Vec::new(); + let module_op = module_ptr.deref(&ctx); + let region = module_op.get_region(0); + let block = region.deref(&ctx).iter(&ctx).next().unwrap(); + for op in block.deref(&ctx).iter(&ctx) { + let Some(func_op) = Operation::get_op::(op, &ctx) else { + continue; + }; + if func_op.get_symbol_name(&ctx).to_string() != func_name { + continue; + } + let func_region = func_op.get_operation().deref(&ctx).get_region(0); + for func_block in func_region.deref(&ctx).iter(&ctx) { + for body_op in func_block.deref(&ctx).iter(&ctx) { + if let Some(icmp) = Operation::get_op::(body_op, &ctx) { + let lhs_ty = body_op.deref(&ctx).get_operand(0).get_type(&ctx); + icmps.push((icmp.predicate(&ctx), lhs_ty)); + } + } + } + } + + let i1: pliron::context::Ptr = bool_ty.into(); + assert_eq!( + icmps, + vec![(ICmpPredicateAttr::EQ, i1), (ICmpPredicateAttr::ULT, i1),], + "bool-phi comparisons must lower to `icmp eq i1` and `icmp ult i1`" + ); + Ok(()) +} From 3583fce555082ed5577e25fbf62dc6a6aa372011 Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Fri, 12 Jun 2026 12:13:58 +0530 Subject: [PATCH 4/5] test(examples): add bool_phi_cmp short-circuit comparison example End-to-end cover for PR #141 through the full device pipeline: a bool phi produced by short-circuit || feeds both == and < comparisons, and the lanes cover all four (p, q) truth combinations. Before the fallback in is_signed_int_op this kernel failed device codegen with "expected IntegerType or MirPtrType operand in arithmetic op"; the emitted IR now contains icmp eq i1 and icmp ult i1 over the phi. --- .../examples/bool_phi_cmp/Cargo.lock | 424 ++++++++++++++++++ .../examples/bool_phi_cmp/Cargo.toml | 12 + .../examples/bool_phi_cmp/src/main.rs | 86 ++++ 3 files changed, 522 insertions(+) create mode 100644 crates/rustc-codegen-cuda/examples/bool_phi_cmp/Cargo.lock create mode 100644 crates/rustc-codegen-cuda/examples/bool_phi_cmp/Cargo.toml create mode 100644 crates/rustc-codegen-cuda/examples/bool_phi_cmp/src/main.rs diff --git a/crates/rustc-codegen-cuda/examples/bool_phi_cmp/Cargo.lock b/crates/rustc-codegen-cuda/examples/bool_phi_cmp/Cargo.lock new file mode 100644 index 0000000000..60be1b8bd6 --- /dev/null +++ b/crates/rustc-codegen-cuda/examples/bool_phi_cmp/Cargo.lock @@ -0,0 +1,424 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "bool_phi_cmp" +version = "0.1.0" +dependencies = [ + "cuda-core", + "cuda-device", + "cuda-host", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "cuda-bindings" +version = "0.2.1" +dependencies = [ + "bindgen", +] + +[[package]] +name = "cuda-core" +version = "0.2.1" +dependencies = [ + "anyhow", + "cuda-bindings", + "half", + "oxide-artifacts", +] + +[[package]] +name = "cuda-device" +version = "0.2.1" +dependencies = [ + "cuda-macros", +] + +[[package]] +name = "cuda-host" +version = "0.2.1" +dependencies = [ + "cuda-core", + "cuda-macros", + "half", + "libnvvm-sys", + "nvjitlink-sys", + "thiserror", +] + +[[package]] +name = "cuda-macros" +version = "0.2.1" +dependencies = [ + "proc-macro2", + "quote", + "reserved-oxide-symbols", + "syn", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libnvvm-sys" +version = "0.2.1" +dependencies = [ + "libloading", + "thiserror", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nvjitlink-sys" +version = "0.2.1" +dependencies = [ + "libloading", + "thiserror", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "crc32fast", + "hashbrown 0.15.5", + "indexmap", + "memchr", +] + +[[package]] +name = "oxide-artifacts" +version = "0.2.1" +dependencies = [ + "object", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "reserved-oxide-symbols" +version = "0.2.1" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/crates/rustc-codegen-cuda/examples/bool_phi_cmp/Cargo.toml b/crates/rustc-codegen-cuda/examples/bool_phi_cmp/Cargo.toml new file mode 100644 index 0000000000..99846e1ff7 --- /dev/null +++ b/crates/rustc-codegen-cuda/examples/bool_phi_cmp/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "bool_phi_cmp" +version = "0.1.0" +edition = "2024" +license = "Apache-2.0" + +[workspace] + +[dependencies] +cuda-device = { path = "../../../cuda-device" } +cuda-host = { path = "../../../cuda-host" } +cuda-core = { path = "../../../cuda-core" } diff --git a/crates/rustc-codegen-cuda/examples/bool_phi_cmp/src/main.rs b/crates/rustc-codegen-cuda/examples/bool_phi_cmp/src/main.rs new file mode 100644 index 0000000000..304e8983d4 --- /dev/null +++ b/crates/rustc-codegen-cuda/examples/bool_phi_cmp/src/main.rs @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Comparisons on a bool produced by short-circuit `||` (PR #141). + * + * A bool assigned in two arms of a short-circuit `||`/`&&` becomes a + * block argument (phi) after mem2reg. Bools are signless i1 in + * dialect-mir, which `can_convert_type` rejects, so DialectConversion + * records no pre-conversion type for the phi. `is_signed_int_op` used to + * fail the whole lowering for such operands: + * + * expected IntegerType or MirPtrType operand in arithmetic op + * + * It now falls back to the live operand type and lowers the comparison + * as unsigned (`icmp eq i1` / `icmp ult i1`). + * + * The kernel feeds the phi into both `==` and `<`; the inputs cover all + * four (p, q) truth combinations so a wrong predicate fails loudly. + * + * Build: + * cargo oxide run bool_phi_cmp + */ + +use cuda_core::{CudaContext, DeviceBuffer, LaunchConfig}; +use cuda_device::{DisjointSlice, kernel, thread}; +use cuda_host::cuda_module; + +#[cuda_module] +mod kernels { + use super::*; + + /// `p` is the short-circuit bool phi; `q` is an independent bool. + /// Encodes both comparisons in one lane: `(p == q) + 2 * (p < q)`. + #[kernel] + pub fn bool_phi_cmp(a: &[u32], b: &[u32], mut out: DisjointSlice) { + let idx = thread::index_1d(); + let i = idx.get(); + if let Some(e) = out.get_mut(idx) { + let p = a[i] > 1 || b[i] > 2; + let q = a[i] == 0; + *e = (p == q) as u32 + 2 * ((p < q) as u32); + } + } +} + +fn main() { + let ctx = CudaContext::new(0).expect("CUDA init"); + let stream = ctx.default_stream(); + let module = kernels::load(&ctx).expect("Load embedded PTX"); + + // Lanes cover all four (p, q) combinations: + // i=0: a=0, b=0 -> p=false, q=true -> eq=0, lt=1 -> 2 + // i=1: a=2, b=0 -> p=true, q=false -> eq=0, lt=0 -> 0 + // i=2: a=0, b=5 -> p=true, q=true -> eq=1, lt=0 -> 1 + // i=3: a=1, b=1 -> p=false, q=false -> eq=1, lt=0 -> 1 + let a = vec![0u32, 2, 0, 1]; + let b = vec![0u32, 0, 5, 1]; + let expect = vec![2u32, 0, 1, 1]; + + let dev_a = DeviceBuffer::from_host(&stream, &a).unwrap(); + let dev_b = DeviceBuffer::from_host(&stream, &b).unwrap(); + let mut dev_out = DeviceBuffer::::zeroed(&stream, a.len()).unwrap(); + + module + .bool_phi_cmp( + &stream, + LaunchConfig::for_num_elems(a.len() as u32), + &dev_a, + &dev_b, + &mut dev_out, + ) + .expect("launch"); + + let out = dev_out.to_host_vec(&stream).unwrap(); + println!("got : {out:?}"); + println!("expect : {expect:?}"); + if out == expect { + println!("SUCCESS"); + } else { + println!("FAILURE: bool-phi comparison produced wrong lanes"); + std::process::exit(1); + } +} From 50e84be1ab1ed78513aa45e3bca2f4e471d1d5f3 Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Fri, 12 Jun 2026 14:39:25 +0530 Subject: [PATCH 5/5] fix(examples): allow the intentional bool order comparison in bool_phi_cmp clippy::bool_comparison wants p < q rewritten as !p & q, but the comparison shape is the regression under test: it must reach mir-lower as an order comparison on an untracked i1 phi and lower to icmp ult i1. --- crates/rustc-codegen-cuda/examples/bool_phi_cmp/src/main.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/rustc-codegen-cuda/examples/bool_phi_cmp/src/main.rs b/crates/rustc-codegen-cuda/examples/bool_phi_cmp/src/main.rs index 304e8983d4..b5f016544a 100644 --- a/crates/rustc-codegen-cuda/examples/bool_phi_cmp/src/main.rs +++ b/crates/rustc-codegen-cuda/examples/bool_phi_cmp/src/main.rs @@ -34,6 +34,10 @@ mod kernels { /// `p` is the short-circuit bool phi; `q` is an independent bool. /// Encodes both comparisons in one lane: `(p == q) + 2 * (p < q)`. + // The bool `<` is the regression under test: it must reach mir-lower as a + // comparison on an untracked i1 phi and lower to `icmp ult i1`. Clippy's + // simplification (`!p & q`) would change the MIR shape and skip that path. + #[allow(clippy::bool_comparison)] #[kernel] pub fn bool_phi_cmp(a: &[u32], b: &[u32], mut out: DisjointSlice) { let idx = thread::index_1d();