diff --git a/cuda-oxide/crates/mir-importer/src/translator/rvalue/aggregate.rs b/cuda-oxide/crates/mir-importer/src/translator/rvalue/aggregate.rs index 165fba12bb..73bb527802 100644 --- a/cuda-oxide/crates/mir-importer/src/translator/rvalue/aggregate.rs +++ b/cuda-oxide/crates/mir-importer/src/translator/rvalue/aggregate.rs @@ -589,6 +589,30 @@ pub(super) fn translate_aggregate_rvalue( })?; let tuple_ty = types::translate_type(ctx, &rust_tuple_ty)?; + // Normalize pointer representations to the tuple's declared + // element types before constructing the aggregate. + let expected_types = { + let tuple_ref = tuple_ty.deref(ctx); + tuple_ref + .downcast_ref::() + .expect("translated tuple must have MirTupleType") + .get_types() + .to_vec() + }; + + for (value, expected_type) in element_values.iter_mut().zip(expected_types) { + let (normalized, prev_after_cast) = cast_to_expected_pointer_type_if_needed( + ctx, + *value, + expected_type, + block_ptr, + current_prev_op, + loc.clone(), + ); + *value = normalized; + current_prev_op = prev_after_cast; + } + // Create mir.construct_tuple operation use dialect_mir::ops::MirConstructTupleOp; diff --git a/cuda-oxide/crates/mir-importer/src/translator/rvalue/coerce.rs b/cuda-oxide/crates/mir-importer/src/translator/rvalue/coerce.rs index d9a3b4913d..40820593cc 100644 --- a/cuda-oxide/crates/mir-importer/src/translator/rvalue/coerce.rs +++ b/cuda-oxide/crates/mir-importer/src/translator/rvalue/coerce.rs @@ -342,6 +342,48 @@ mod tests { use dialect_mir::types::{MirPointerKind, MirPtrType}; use pliron::builtin::types::{IntegerType, Signedness}; + #[test] + fn expected_pointer_normalization_preserves_cluster_shared_raw_const() { + let mut ctx = Context::new(); + crate::translator::register_dialects(&mut ctx); + + let pointee: TypeHandle = IntegerType::get(&ctx, 32, Signedness::Unsigned).into(); + + let source_ty: TypeHandle = MirPtrType::get_with_kind( + &mut ctx, + pointee, + false, + dialect_mir::types::address_space::CLUSTER_SHARED, + MirPointerKind::RawConst, + ) + .into(); + + let target_ty: TypeHandle = + MirPtrType::get_generic_with_kind(&mut ctx, pointee, false, MirPointerKind::RawConst) + .into(); + + let block = BasicBlock::new(&mut ctx, None, vec![source_ty]); + let source_value = block.deref(&ctx).get_argument(0); + + let (normalized, last_op) = cast_to_expected_pointer_type_if_needed( + &mut ctx, + source_value, + target_ty, + block, + None, + Location::Unknown, + ); + + assert_eq!(normalized.get_type(&ctx), target_ty); + + let cast_op = last_op.expect("AS7 to generic normalization must insert a pointer cast"); + + assert!( + Operation::get_op::(cast_op, &ctx).is_some(), + "normalization must produce mir.cast" + ); + } + #[test] fn expected_pointer_normalization_rejects_concrete_kind_change() { let mut ctx = Context::new(); diff --git a/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/README.md b/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/README.md index e87b4f8ca4..6e543ac139 100644 --- a/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/README.md +++ b/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/README.md @@ -9,7 +9,7 @@ Demonstrates Thread Block Clusters, a Hopper feature that enables direct shared 1. **test_cluster_compile_time**: Compile-time cluster configuration with `#[cluster_launch]` 2. **test_cluster_intrinsics**: Cluster special registers (ctaid, nctaid, rank, size) 3. **test_cluster_sync**: Cluster-wide synchronization -4. **test_dsmem_ring_exchange**: Distributed shared memory read through `map_shared_rank` + dereference +4. **test_dsmem_ring_exchange**: Distributed shared memory read through `map_shared_rank` + dereference, with the mapped pointer also round-tripped through a `(ptr, rank)` tuple and a device fn (`bounce_cluster_pair`) and compared against the direct read 5. **test_dsmem_reduction**: All-to-one reduction using `dsmem_read_u32` 6. **test_dsmem_mapped_store**: Distributed shared memory write through `map_shared_rank_mut` + dereference diff --git a/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/src/main.rs b/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/src/main.rs index 0e78973abd..d9d2a5c18f 100644 --- a/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/src/main.rs +++ b/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/src/main.rs @@ -15,7 +15,7 @@ //! Uses unified compilation: single `cargo oxide run cluster` use core::ptr::{addr_of, addr_of_mut}; -use cuda_device::{DisjointSlice, SharedArray, cluster, cluster_launch, kernel, thread}; +use cuda_device::{DisjointSlice, SharedArray, cluster, cluster_launch, device, kernel, thread}; use cuda_host::cuda_module; // ============================================================================ @@ -136,6 +136,12 @@ mod kernels { // Test 3: Distributed Shared Memory (Ring Exchange) // ============================================================================ + #[inline(never)] + #[device] + fn bounce_cluster_pair(pair: (*const u32, u64)) -> (*const u32, u64) { + pair + } + /// Test kernel for distributed shared memory via map_shared_rank() + dereference. #[kernel] #[cluster_launch(4, 1, 1)] @@ -160,7 +166,16 @@ mod kernels { let neighbor_rank = (my_rank + 1) % cluster_size; let neighbor_ptr = unsafe { cluster::map_shared_rank(addr_of!(SHMEM) as *const u32, neighbor_rank) }; - let neighbor_value = unsafe { *neighbor_ptr }; + let pair = bounce_cluster_pair((neighbor_ptr, neighbor_rank as u64)); + + let direct = unsafe { *neighbor_ptr }; + let bounced = unsafe { *pair.0 }; + + let neighbor_value = if bounced == direct && pair.1 == neighbor_rank as u64 { + bounced + } else { + u32::MAX + }; let idx = my_rank as usize; if idx < output.len() { diff --git a/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/verify-code-shape.sh b/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/verify-code-shape.sh index dd2efc8080..7861d15124 100755 --- a/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/verify-code-shape.sh +++ b/cuda-oxide/crates/rustc-codegen-cuda/examples/cluster/verify-code-shape.sh @@ -71,4 +71,12 @@ require_entry_shape test_dsmem_ring_exchange \ require_entry_shape test_dsmem_mapped_store \ "cluster-shared remote store" 'st\.shared::cluster\.' +# LLVM opt -O2 inlines the private #[inline(never)] helper because noinline is +# not propagated, so this check reads the pre-opt cluster.ll; the optimized PTX +# does not retain the call. +require_ll_shape "tuple round-trip LLVM call" \ + 'call \{ ptr, i64 \} @bounce_cluster_pair\(' +require_ll_shape "cluster-shared pointer normalized before tuple construction" \ + 'addrspacecast ptr addrspace\(7\) %[A-Za-z0-9_.]+ to ptr' + echo "cluster code shape: PASS"