mod goals;
mod placement;
use syn::spanned::Spanned;
use syn::ExprCall;
use quote::ToTokens;
use super::_function::CUDATileFunctionCompiler;
use super::_value::{CompilerContext, TileRustValue};
use super::shared_types::Kind;
use crate::error::JITError;
use crate::generics::GenericVars;
use cutile_ir::ir::{BlockId, Module};
impl<'m> CUDATileFunctionCompiler<'m> {
fn count_discharged(&self) {
self.check_stats
.discharged
.set(self.check_stats.discharged.get() + 1);
}
pub(super) fn compile_check_partition_access(
&self,
module: &mut Module,
block_id: BlockId,
call_expr: &ExprCall,
generic_vars: &GenericVars,
ctx: &mut CompilerContext,
) -> Result<Option<TileRustValue>, JITError> {
let mut args =
self.compile_call_args(module, block_id, &call_expr.args, generic_vars, ctx)?;
let partition_value = args.remove(0);
let index_value = args.remove(0);
if partition_value.kind != Kind::StructuredType {
return self.jit_error_result(
&call_expr.span(),
&format!(
"expected a structured or primitive type for first argument of `{}`, got {:?}",
&call_expr.to_token_stream().to_string(),
partition_value.kind
),
);
}
if index_value.kind != Kind::Compound {
return self.jit_error_result(
&call_expr.span(),
&format!(
"Unexpected kind for arg 1 in {}",
&call_expr.to_token_stream().to_string()
),
);
}
let (static_tile, static_shape, dim_map) =
self.partition_static_geometry(&partition_value, &call_expr.span())?;
let tensor_shape_value = partition_value
.get_type_meta_field("tensor_view.shape()")
.ok_or_else(|| {
self.jit_error(
&call_expr.span(),
"Failed to obtain type meta field tensor_view.shape().",
)
})?;
let Some(tensor_shape_values) = tensor_shape_value.fields.as_ref() else {
return self.jit_error_result(
&call_expr.span(),
"Expected fields for tensor shape expression.",
);
};
let Some(shape_dims) = tensor_shape_values.get("dims") else {
return self.jit_error_result(
&call_expr.span(),
"Expected dims field for shape expression.",
);
};
let Some(dynamic_shape) = shape_dims.values.as_ref() else {
return self.jit_error_result(&call_expr.span(), "expected a compound (tuple) value");
};
let Some(indexes) = index_value.values.as_ref() else {
return self.jit_error_result(&call_expr.span(), "expected a compound (tuple) value");
};
let len = static_tile.len();
if len != indexes.len() || len != static_shape.len() {
return self.jit_error_result(
&call_expr.span(),
&format!(
"Unexpected tile ({}), shape ({}), or index ({}) length mismatch.",
len,
static_shape.len(),
indexes.len()
),
);
}
for (axis, index) in indexes.iter().enumerate() {
let axis_goals = self.form_axis_goals(
&partition_value,
&static_tile,
&static_shape,
&dim_map,
axis,
index,
);
if !crate::cuda_tile_runtime_utils::force_device_checks() {
if self.discharge_by_axis_provenance(&axis_goals, &partition_value) {
self.count_discharged();
continue;
}
match goals::fold_static(&axis_goals) {
Some(Ok(())) => {
self.count_discharged();
continue;
}
Some(Err(violation)) => {
return self.jit_error_result(
&call_expr.span(),
&format!(
"Bounds check failed: 0 <= {} && {} < {}",
violation.min, violation.max, violation.num_tiles
),
);
}
None => {}
}
if self.discharge_by_block_id_axiom(&axis_goals, &partition_value) {
self.count_discharged();
continue;
}
}
if !crate::cuda_tile_runtime_utils::force_device_checks()
&& self.hoist_zero_coordinate_nonempty_extent(&axis_goals, &partition_value)
{
self.count_discharged();
continue;
}
let dynamic_extent = if axis_goals.static_extent.is_none() {
Some(self.resolve_dynamic_extent(
&axis_goals,
&static_shape,
dynamic_shape,
call_expr,
)?)
} else {
None
};
self.place_residual_check(
module,
block_id,
&axis_goals,
dynamic_extent,
generic_vars,
ctx,
&call_expr.span(),
)?;
}
Ok(None)
}
pub(super) fn compile_check_bounded_partition_access(
&self,
module: &mut Module,
block_id: BlockId,
call_expr: &ExprCall,
generic_vars: &GenericVars,
ctx: &mut CompilerContext,
) -> Result<Option<TileRustValue>, JITError> {
if call_expr.args.len() != 2 {
return self.jit_error_result(
&call_expr.span(),
&format!(
"`check_bounded_partition_access` expects 2 arguments, got {}",
call_expr.args.len()
),
);
}
let mut args =
self.compile_call_args(module, block_id, &call_expr.args, generic_vars, ctx)?;
let partition = args.remove(0);
let coord = args.remove(0);
let Some(bound_axes) = partition.bounded_axes.as_ref() else {
return self.jit_error_result(
&call_expr.args[0].span(),
"bounded partition load requires bounds established by `Partition::with_bounds`",
);
};
let Some(fields) = coord.fields.as_ref() else {
return self.jit_error_result(
&call_expr.args[1].span(),
"bounded partition load requires a coordinate created by `coord(...)`",
);
};
let Some(coords) = fields.get("coords") else {
return self.jit_error_result(
&call_expr.args[1].span(),
"coordinate is missing its metadata",
);
};
let Some(coord_values) = coords.values.as_ref() else {
return self.jit_error_result(
&call_expr.args[1].span(),
"coordinates must be a compound value",
);
};
if coord_values.len() != bound_axes.len() {
return self.jit_error_result(
&call_expr.args[1].span(),
&format!(
"coordinate rank {} does not match bounded partition rank {}",
coord_values.len(),
bound_axes.len()
),
);
}
let (static_tile, static_shape, dim_map) =
self.partition_static_geometry(&partition, &call_expr.args[0].span())?;
for (axis, (coord_value, bound_origin)) in
coord_values.iter().zip(bound_axes.iter()).enumerate()
{
let axis_goals = self.form_axis_goals(
&partition,
&static_tile,
&static_shape,
&dim_map,
axis,
coord_value,
);
match goals::brand_of(&axis_goals, bound_origin) {
Some(goals::Brand::Matched) => {
self.count_discharged();
continue;
}
Some(goals::Brand::Foreign) => {
return self.jit_error_result(
&call_expr.args[1].span(),
&format!(
"bounded partition coordinate axis {axis} was produced by a different dimension"
),
);
}
None => {}
}
match goals::fold_static(&axis_goals) {
Some(Ok(())) => {
self.count_discharged();
continue;
}
Some(Err(violation)) => {
return self.jit_error_result(
&call_expr.args[1].span(),
&format!(
"bounded partition coordinate axis {axis}: constant range [{}, {}] is not within the {}-tile grid",
violation.min, violation.max, violation.num_tiles
),
);
}
None => {}
}
if self.hoist_zero_coordinate_nonempty_extent(&axis_goals, &partition) {
self.count_discharged();
continue;
}
return self.jit_error_result(
&call_expr.args[1].span(),
&format!(
"bounded partition coordinate axis {axis} must come from iterating the matching dimension or be a constant within the axis's static tile grid"
),
);
}
Ok(None)
}
}