Skip to main content

cubecl_ir/interfaces/
memory_slot.rs

1use derive_new::new;
2use pliron::{
3    attribute::AttrObj,
4    basic_block::BasicBlock,
5    opts::mem2reg::AllocInfo,
6    region::Region,
7    utils::table::{HMap, SmallMap, SmallSet},
8};
9
10use crate::prelude::*;
11
12pub type LogicalResult = core::result::Result<(), ()>;
13
14#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
15pub enum DeletionKind {
16    Keep,
17    Delete,
18}
19
20#[op_interface]
21pub trait PromotableRegionOpInterface {
22    verify_op_succ!();
23
24    /// Returns true if `region` (a child of this op) can be analysed for
25    /// promotion with respect to `alloc`.
26    /// `has_value_stores` is a hint: true when the region contains stores to alloc.
27    fn is_region_promotable(
28        &self,
29        ctx: &Context,
30        alloc: &AllocInfo,
31        region: Ptr<Region>,
32        has_value_stores: bool,
33    ) -> bool;
34
35    /// Called before descending into nested regions.
36    /// `reaching_def` is the value in `slot` on entry to this op.
37    /// Populate `regions_to_process` with the reaching def each region starts with.
38    /// You may mutate the op in place, but do NOT delete ops or touch terminators.
39    fn setup_promotion(
40        &self,
41        ctx: &mut Context,
42        alloc: &AllocInfo,
43        reaching_def: Value,
44        has_value_stores: bool,
45        regions_to_process: &mut SmallMap<Ptr<Region>, Value, 2>,
46    );
47
48    /// Called after reaching defs are computed for all regions, but before
49    /// blocking uses are removed. Returns the new reaching def at the op's exit.
50    /// Mutation is allowed, but you must not change control flow or add ops that
51    /// interact with the slot's value.
52    fn finalize_promotion(
53        &self,
54        ctx: &mut Context,
55        alloc: &AllocInfo,
56        entry_reaching_def: Value,
57        has_value_stores: bool,
58        reaching_at_block_end: &HMap<Ptr<BasicBlock>, Value>,
59    ) -> Value;
60}
61
62/// Describes a type that can be broken down into indexable sub-element types.
63#[type_interface]
64pub trait DestructurableTypeInterface {
65    verify_ty_succ!();
66
67    /// Destructures the type into subelements into a map of indices to
68    /// types of subelements. Returns nothing if the type cannot be destructured.
69    fn subelement_index_map(&self, ctx: &Context) -> Option<HMap<AttrObj, TypeHandle>>;
70
71    /// Indicates which type is held at the provided index, returning None
72    /// if no type could be computed. While this can return information
73    /// even when the type cannot be completely destructured, it must be coherent
74    /// with the types returned by `subelement_index_map` when they exist.
75    fn type_at_index(&self, ctx: &Context, index: &AttrObj) -> TypeHandle;
76}
77
78/// Describes operations creating values of aggregates that can be
79/// destructured into multiple smaller values.
80#[op_interface]
81pub trait DestructurableConstructorOpInterface {
82    verify_op_succ!();
83
84    /// Returns the list of value for which destructuring should be attempted,
85    /// specifying in which way the value should be destructured into subvalues.
86    /// This computes the type of the value for each subvalue to be generated. The type of the value
87    /// must implement [`DestructurableTypeInterface`].
88    ///
89    /// No IR mutation is allowed in this method.
90    fn destructurable_values(&self, ctx: &Context) -> Vec<DestructurableValueSlot>;
91
92    /// Destructures this value into multiple subvalues. The original value must still exist
93    /// at the end of this call. Only generates subvalues for the indices found in
94    /// `used_indices` since all other subvalues are unused.
95    ///
96    /// The rewriter is located before this op.
97    fn destructure(
98        &self,
99        ctx: &mut Context,
100        value: &DestructurableValueSlot,
101        used_indices: &SmallSet<AttrObj, 8>,
102        rewriter: &mut PassRewriter,
103        new_constructors: &mut Vec<TraitOp<dyn DestructurableConstructorOpInterface>>,
104    ) -> HMap<AttrObj, ValueSlot>;
105
106    /// Hook triggered once the destructuring of a value is complete, meaning the
107    /// original value is no longer being referred to and could be deleted.
108    /// This will only be called for values declared by this operation.
109    ///
110    /// Must return a new destructurable constructor op if this hook creates
111    /// a new destructurable op, `None` otherwise.
112    fn handle_destructuring_complete(
113        &self,
114        ctx: &mut Context,
115        value: &DestructurableValueSlot,
116        rewriter: &mut PassRewriter,
117    ) -> Option<TraitOp<dyn DestructurableConstructorOpInterface>>;
118}
119
120/// Describes operations that can access a sub-element of a destructurable value.
121#[op_interface]
122pub trait DestructurableAccessorOpInterface {
123    verify_op_succ!();
124
125    /// For a given destructurable value, returns whether this operation can
126    /// rewire its uses of the value to use the values generated after
127    /// destructuring. This may involve creating new operations.
128    ///
129    /// This method must also register the indices it will access within the
130    /// `used_indices` set. If the accessor generates new values mapping to
131    /// subelements, they must be registered in `must_be_safely_used` to ensure
132    /// they are used in a safe manner.
133    ///
134    /// No IR mutation is allowed in this method.
135    fn can_rewire(
136        &self,
137        ctx: &Context,
138        value: &DestructurableValueSlot,
139        used_indices: &mut SmallSet<AttrObj, 8>,
140        must_be_safely_used: &mut Vec<ValueSlot>,
141    ) -> bool;
142
143    /// Rewires the use of a slot to the generated subvalues, without deleting
144    /// any operation. Returns whether the accessor should be deleted.
145    ///
146    /// Deletion of operations is not allowed, only the accessor can be
147    /// scheduled for deletion by returning the appropriate value.
148    fn rewire(
149        &self,
150        ctx: &mut Context,
151        value: &DestructurableValueSlot,
152        subvalues: &HMap<AttrObj, ValueSlot>,
153        rewriter: &mut PassRewriter,
154    ) -> DeletionKind;
155}
156
157#[op_interface]
158pub trait SafeMemorySlotAccessOpInterface {
159    verify_op_succ!();
160
161    #[allow(clippy::result_unit_err)]
162    /// Returns whether all accesses in this operation to the provided value are
163    /// done in a safe manner. To be safe, the access must only access the value
164    /// inside the bounds that its type implies.
165    ///
166    /// If the safety of the accesses depends on the safety of the accesses to
167    /// further value, the result of this method will be conditioned to
168    /// the safety of the accesses to the value added by this method to
169    /// `must_be_safely_used`.
170    ///
171    /// No IR mutation is allowed in this method.
172    fn ensure_only_safe_accesses(
173        &self,
174        ctx: &Context,
175        value: &ValueSlot,
176        must_be_safely_used: &mut Vec<ValueSlot>,
177    ) -> LogicalResult;
178}
179
180#[derive(new, Debug)]
181pub struct ValueSlot {
182    pub value: Value,
183    pub elem_ty: TypeHandle,
184}
185
186#[derive(Debug)]
187pub struct DestructurableValueSlot {
188    pub slot: ValueSlot,
189    pub subelement_types: HMap<AttrObj, TypeHandle>,
190}