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}