Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cutile-rs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

### Fixed

- Const-generic array bindings retain their function or method signature as
their source origin, and generated reduction bodies retain the reduction
call's location and inline chain.
- Failed host launch checks report their original accesses and inline callers,
retaining distinct origins when equivalent predicates share one comparison.

### Changed

- cuTile Rust now lives in the [NVIDIA/cuda-rust](https://github.com/NVIDIA/cuda-rust)
Expand Down
9 changes: 9 additions & 0 deletions cutile-rs/cutile-book/guide/debugging-and-profiling.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,15 @@ when inlined, including across modules. Core operations are attributed to
the user's call site. Multiple generated instructions can still map to one
Rust line; this is not a one-to-one mapping, especially with optimization.

Generated const-generic array bindings use the containing function or method's
signature as their source origin. Generated reduction bodies use the reduction
call's location, including its inline caller chain.

Checks moved to the host launcher report the original access and its inline
callers when validation fails, including in builds without device debug info.
If several accesses share one launch check, its diagnostic retains each
distinct source origin while the predicate is evaluated only once.

## Profiling

Use NVIDIA **Nsight Compute** (`ncu`, reports opened with `ncu-ui`) for
Expand Down
2 changes: 1 addition & 1 deletion cutile-rs/cutile-compiler/src/compile_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ impl<F: Fn() -> crate::ast::Module> KernelCompiler<F> {
hoisted: compiler.check_stats.hoisted.get(),
in_place: compiler.check_stats.in_place.get(),
};
let launch_checks = compiler.launch_checks.borrow().clone();
let launch_checks = compiler.launch_checks();
Ok(CompileArtifacts {
module,
bytecode_version: self.bytecode_version,
Expand Down
31 changes: 21 additions & 10 deletions cutile-rs/cutile-compiler/src/compiler/_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//! the old CUDATileFunctionCompiler.

use super::_module::CUDATileModules;
use super::launch_checks::LaunchChecks;
use crate::ast::{SourceLocation, SpanBase};
use crate::bounds::Bounds;
use crate::error::{JITError, SpannedJITError};
Expand Down Expand Up @@ -72,7 +73,7 @@ pub struct CUDATileFunctionCompiler<'m> {
/// (`Resolution::Launch`). Interior-mutable because compile passes take
/// `&self`; merged into the `Validator` in [`Self::get_validator`] and run
/// once per launch by the host `validate_launch`.
pub(crate) launch_checks: RefCell<Vec<cuda_async::predicate::LaunchCheck>>,
launch_checks: RefCell<LaunchChecks>,
pub(crate) gpu_name: String,
pub(crate) target_capabilities: Option<cutile_ir::capabilities::TargetCapabilities>,
pub(crate) optimization_hints: OptimizationHints,
Expand Down Expand Up @@ -187,6 +188,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
a_axis: usize,
b_tensor: &str,
b_axis: usize,
span: &proc_macro2::Span,
) -> bool {
use cuda_async::predicate::{Atom, Predicate, Term};
// Resolve both tensor names to param indices; if either is not a
Expand All @@ -212,6 +214,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
self.lower_obligation(
predicate,
format!("dim({a_tensor}, {a_axis}) == dim({b_tensor}, {b_axis})"),
span,
)
}

Expand All @@ -238,6 +241,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
b_tensor: &str,
b_axis: usize,
tile: i32,
span: &proc_macro2::Span,
) -> bool {
use crate::passes::obligation::{resolve, Obligation, Resolution};
use cuda_async::predicate::{Atom, Predicate, Term};
Expand Down Expand Up @@ -296,6 +300,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
format!(
"ceil(dim({a_tensor}, {a_axis})/{tile}) <= ceil(dim({b_tensor}, {b_axis})/{tile})"
),
span,
)
}

Expand All @@ -315,25 +320,28 @@ impl<'m> CUDATileFunctionCompiler<'m> {
&self,
predicate: cuda_async::predicate::Predicate,
cause: impl Into<String>,
span: &proc_macro2::Span,
) -> bool {
use crate::passes::obligation::{resolve, Obligation, Resolution};
let obligation = Obligation::new(predicate, cause);
match resolve(&obligation, &self.assumptions) {
Resolution::Jit => true,
Resolution::Launch(check) => {
// Canonical predicates make duplicate detection exact. Two
// obligations reducing to the same host compare (e.g. a shared
// `Dim`'s divisibility, once per binding) cost one check.
let mut checks = self.launch_checks.borrow_mut();
if !checks.iter().any(|c| c.predicate == check.predicate) {
checks.push(check);
}
self.launch_checks
.borrow_mut()
.insert(check, &self.ir_location(span));
true
}
Resolution::Device => false,
}
}

/// Export checks with their source origins rendered in the existing host
/// diagnostic field. Structured origins remain internal to the compiler.
pub(crate) fn launch_checks(&self) -> Vec<cuda_async::predicate::LaunchCheck> {
self.launch_checks.borrow().to_host_checks()
}

/// The kernel-visible extent of `partition`'s axis `axis`, taken from the
/// *declared* parameter shape.
///
Expand Down Expand Up @@ -660,7 +668,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
assumptions,
param_index,
param_is_mutable,
launch_checks: RefCell::new(Vec::new()),
launch_checks: RefCell::new(LaunchChecks::default()),
gpu_name,
target_capabilities: None,
optimization_hints,
Expand Down Expand Up @@ -1177,6 +1185,9 @@ impl<'m> CUDATileFunctionCompiler<'m> {

// Add arrays as variables.
for (key, value) in generic_vars.ordered_inst_array() {
// Binding materialization has no expression in the source. Use
// the user signature, not the generated ABI wrapper's span.
let _source = self.push_opaque_call_site(&self._function.sig.span());
let arr_expr = syn::parse2::<syn::Expr>(format!("{value:?}").parse().unwrap()).unwrap();
let arr_ty =
syn::parse2::<syn::Type>(format!("[i32;{}]", value.len()).parse().unwrap())
Expand Down Expand Up @@ -1253,7 +1264,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
let mut validator = self.validator.clone();
// Surface the launch checks hoisted during compilation so the host runs
// them at launch.
validator.launch_checks = self.launch_checks.borrow().clone();
validator.launch_checks = self.launch_checks();
validator
}

Expand Down
8 changes: 6 additions & 2 deletions cutile-rs/cutile-compiler/src/compiler/checks/goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
&self,
goals: &AxisGoals<'_>,
partition: &TileRustValue,
span: &Span,
) -> bool {
if let (Some(origin), Some(target)) = (
goals.index.partition_axis_origin.as_ref(),
Expand All @@ -192,6 +193,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
target,
goals.extent_axis,
goals.tile,
span,
)
{
return true;
Expand Down Expand Up @@ -238,6 +240,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
&self,
goals: &AxisGoals<'_>,
partition: &TileRustValue,
span: &Span,
) -> bool {
use cuda_async::predicate::{Atom, Predicate, Term};
let Some(term) = goals.index.term.as_ref() else {
Expand Down Expand Up @@ -275,7 +278,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
"num_tile_blocks({k}) <= ceil(extent({tensor}, {})/{})",
goals.extent_axis, goals.tile
);
self.lower_obligation(le, cause)
self.lower_obligation(le, cause, span)
}

/// The entailment/launch rung for a constant zero coordinate against a
Expand All @@ -300,6 +303,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
&self,
goals: &AxisGoals<'_>,
partition: &TileRustValue,
span: &Span,
) -> bool {
use cuda_async::predicate::{Predicate, Term};
if goals.static_extent.is_some() {
Expand All @@ -322,7 +326,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
"partition access on axis {} of `{tensor}` requires a non-empty extent",
goals.extent_axis
);
self.lower_obligation(predicate, cause)
self.lower_obligation(predicate, cause, span)
}

/// The runtime scalar holding the extent for a goal whose static
Expand Down
24 changes: 20 additions & 4 deletions cutile-rs/cutile-compiler/src/compiler/checks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,11 @@ impl<'m> CUDATileFunctionCompiler<'m> {
// Rung 1/3: axis provenance — a same-view iterand, or a
// foreign iterand whose axis a declared root-dimension
// equality relates to this one.
if self.discharge_by_axis_provenance(&axis_goals, &partition_value) {
if self.discharge_by_axis_provenance(
&axis_goals,
&partition_value,
&call_expr.span(),
) {
self.count_discharged();
continue;
}
Expand All @@ -195,7 +199,11 @@ impl<'m> CUDATileFunctionCompiler<'m> {
// grid-bounded by the execution model; the residual
// grid-vs-tile-count claim moves to a launch check
// (goals.rs, discharge_by_block_id_axiom).
if self.discharge_by_block_id_axiom(&axis_goals, &partition_value) {
if self.discharge_by_block_id_axiom(
&axis_goals,
&partition_value,
&call_expr.span(),
) {
self.count_discharged();
continue;
}
Expand All @@ -208,7 +216,11 @@ impl<'m> CUDATileFunctionCompiler<'m> {
// undischarged checks are compile errors rather than device
// placements.
if self.check_opts.relocate_to_launch
&& self.hoist_zero_coordinate_nonempty_extent(&axis_goals, &partition_value)
&& self.hoist_zero_coordinate_nonempty_extent(
&axis_goals,
&partition_value,
&call_expr.span(),
)
{
self.count_discharged();
continue;
Expand Down Expand Up @@ -346,7 +358,11 @@ impl<'m> CUDATileFunctionCompiler<'m> {
}
// Rung 3/4: a constant `[0, 0]` coordinate against a dynamic
// extent reduces to `extent > 0`, a launch-known predicate.
if self.hoist_zero_coordinate_nonempty_extent(&axis_goals, &partition) {
if self.hoist_zero_coordinate_nonempty_extent(
&axis_goals,
&partition,
&call_expr.span(),
) {
self.count_discharged();
continue;
}
Expand Down
14 changes: 10 additions & 4 deletions cutile-rs/cutile-compiler/src/compiler/compile_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1018,7 +1018,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
"`with_bounds` bound {axis}: the partitioned extent must be divisible by the tile \
extent {tile_dim}, or the declared bound undercounts the tiles on axis {axis}"
);
if self.lower_obligation(divisibility, cause) {
if self.lower_obligation(divisibility, cause, span) {
return Ok(());
}
}
Expand Down Expand Up @@ -1056,8 +1056,8 @@ impl<'m> CUDATileFunctionCompiler<'m> {
(c - 1) * tile_dim,
c * tile_dim
);
if self.lower_obligation(above, cause.clone())
&& self.lower_obligation(not_above, cause)
if self.lower_obligation(above, cause.clone(), span)
&& self.lower_obligation(not_above, cause, span)
{
return Ok(());
}
Expand Down Expand Up @@ -1357,7 +1357,13 @@ impl<'m> CUDATileFunctionCompiler<'m> {
}
}
if let (Some(receiver_name), Some(other_name)) = (&receiver_name, &other_name) {
if self.resolve_dim_eq(receiver_name, axis, other_name, axis) {
if self.resolve_dim_eq(
receiver_name,
axis,
other_name,
axis,
&method_call.span(),
) {
self.check_stats
.discharged
.set(self.check_stats.discharged.get() + 1);
Expand Down
2 changes: 2 additions & 0 deletions cutile-rs/cutile-compiler/src/compiler/compile_inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
}
// Add function call CGAs arrays as variables.
for (key, value) in call_generic_vars.ordered_inst_array() {
let _source = self.push_opaque_call_site(&fn_item.sig.span());
let arr_expr = syn::parse2::<Expr>(format!("{value:?}").parse().unwrap()).unwrap();
let arr_ty =
syn::parse2::<Type>(format!("[i32;{}]", value.len()).parse().unwrap()).unwrap();
Expand Down Expand Up @@ -433,6 +434,7 @@ impl<'m> CUDATileFunctionCompiler<'m> {
call_variables.vars.insert(key.to_string(), tr_val);
}
for (key, value) in call_generic_vars.ordered_inst_array() {
let _source = self.push_opaque_call_site(&impl_method.sig.span());
let arr_expr = syn::parse2::<Expr>(format!("{value:?}").parse().unwrap()).unwrap();
let arr_ty =
syn::parse2::<Type>(format!("[i32;{}]", value.len()).parse().unwrap()).unwrap();
Expand Down
3 changes: 3 additions & 0 deletions cutile-rs/cutile-compiler/src/compiler/compile_intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1743,6 +1743,9 @@ impl<'m> CUDATileFunctionCompiler<'m> {
};
// Build the reduce body region.
let region_id = {
// The synthetic closure implements this reduction call;
// its parsed tokens have no independent source location.
let _source = self.push_opaque_call_site(&call_expr.span());
let local_var_types = &[
ir_operand_type.clone(), // operand_i_current_iter
ir_operand_type.clone(), // operand_i_prev_iter
Expand Down
Loading
Loading