1use rucc_ir::{
52 Block, Builder, Extra, Flags, Float, Func, Inst, IntPred, MemInfo, Opcode, Type, Value,
53};
54use rucc_target::Slot;
55
56use super::{added, buffer, info, is_float, offset, width};
57
58pub const STACK: i64 = 0;
60pub const GR_TOP: i64 = 8;
62pub const VR_TOP: i64 = 16;
64pub const GR_OFFS: i64 = 24;
66pub const VR_OFFS: i64 = 28;
68pub const SIZE: u64 = 32;
70
71const WORD: u32 = 8;
73const VECTOR: u32 = 16;
75const IN_REGISTERS: u64 = 16;
77
78#[derive(Clone, Copy)]
80struct Wants {
81 float: bool,
83 slots: u32,
85 even: bool,
87 size: u64,
89 wide: bool,
91}
92
93pub(super) fn next(func: &mut Func, inst: Inst) {
95 let Some(result) = func[inst].first_result else { return };
96 let Some(&list) = func[func[inst].args].first() else { return };
97 let ty = func[result].ty;
98 let Some(block) = func.block_of(inst) else { return };
99 if !ty.is_scalar() || !(ty.is_int() || ty.is_float() || ty.is_ptr()) || ty.bits() > 128 {
100 return;
101 }
102 if ty.is_int() && ty.bits() > 64 {
104 return;
105 }
106 let bytes = u64::from(ty.bits().div_ceil(8)).max(1);
107 let float = ty.is_float();
108 let wants = Wants { float, slots: 1, even: false, size: bytes, wide: bytes > 8 };
109
110 let rest = cut(func, block, inst);
111 let (join, address) = walk(func, block, inst, list, wants, |_, found| found);
112
113 let span = func.span(inst);
114 let mut build = Builder::new(func, join).at(span);
115 let from = build.unary(Opcode::IntToPtr, address, Type::PTR);
116 let align = u32::try_from(bytes.next_power_of_two()).unwrap_or(1);
117 let mem = func.add_mem(info(bytes, align));
118 let args = func.push_values(&[from]);
119 let data = &mut func[inst];
120 data.opcode = Opcode::Load;
121 data.args = args;
122 data.extra = Extra::Mem(mem);
123 data.flags = data.flags.intersection(Flags::legal_on(Opcode::Load));
124 func.append_inst(join, inst);
125 for at in rest {
126 func.append_inst(join, at);
127 }
128}
129
130pub(super) fn object(func: &mut Func, inst: Inst) {
132 let Extra::VaObject(at) = func[inst].extra else { return };
133 let object = func[at];
134 let MemInfo { size, align, .. } = func[object.mem];
135 let slots: Vec<Slot> = func[object.slots].to_vec();
136 let Some(&list) = func[func[inst].args].first() else { return };
137 let Some(block) = func.block_of(inst) else { return };
138 if func[inst].first_result.is_none() {
139 return;
140 }
141 let floats = slots.iter().filter(|&&slot| is_float(slot)).count();
142 let by_reference = slots.is_empty() && size > IN_REGISTERS;
143 let wants = if by_reference {
144 Wants { float: false, slots: 1, even: false, size: u64::from(WORD), wide: false }
145 } else if floats == 0 {
146 let slots = u32::try_from(size.div_ceil(u64::from(WORD))).unwrap_or(0);
147 if slots == 0 || size > IN_REGISTERS {
148 return;
149 }
150 Wants { float: false, slots, even: align >= 16, size, wide: align >= 16 }
151 } else if floats == slots.len() && floats <= 4 {
152 let wide = slots.iter().any(|&slot| width(slot) > u64::from(WORD));
153 let slots = u32::try_from(floats).unwrap_or(0);
154 Wants { float: true, slots, even: false, size, wide }
155 } else {
156 return;
157 };
158 let room = if wants.float && wants.slots > 1 {
162 let Some(room) = buffer(func, inst, size, align.max(WORD)) else { return };
163 Some(room)
164 } else {
165 None
166 };
167
168 let rest = cut(func, block, inst);
169 let copy = |build: &mut Builder<'_>, found: Value| match room {
170 Some(room) => copied(build, found, &slots, room, align.max(WORD)),
171 None => found,
172 };
173 let (join, mut address) = walk(func, block, inst, list, wants, copy);
174 if by_reference {
175 let span = func.span(inst);
176 let mut build = Builder::new(func, join).at(span);
177 let slot = build.unary(Opcode::IntToPtr, address, Type::PTR);
178 let held = build.load(Type::PTR, slot, info(8, 8), Flags::default());
179 address = build.unary(Opcode::PtrToInt, held, Type::int(64));
180 }
181
182 let args = func.push_values(&[address]);
183 let data = &mut func[inst];
184 data.opcode = Opcode::IntToPtr;
185 data.args = args;
186 data.extra = Extra::None;
187 data.flags = data.flags.intersection(Flags::legal_on(Opcode::IntToPtr));
188 func.append_inst(join, inst);
189 for at in rest {
190 func.append_inst(join, at);
191 }
192}
193
194pub(super) fn stacked(func: &mut Func, inst: Inst) {
201 let Extra::VaObject(at) = func[inst].extra else { return };
202 let object = func[at];
203 let MemInfo { size, align, .. } = func[object.mem];
204 let by_reference = func[object.slots].is_empty() && size > IN_REGISTERS;
205 let Some(&list) = func[func[inst].args].first() else { return };
206 let Some(block) = func.block_of(inst) else { return };
207 if func[inst].first_result.is_none() {
208 return;
209 }
210 let wants = if by_reference {
211 Wants { float: false, slots: 1, even: false, size: u64::from(WORD), wide: false }
212 } else {
213 Wants { float: false, slots: 1, even: false, size, wide: align >= 16 }
214 };
215
216 let rest = cut(func, block, inst);
217 let span = func.span(inst);
218 let mut build = Builder::new(func, block).at(span);
219 let mut address = in_memory(&mut build, list, wants);
220 if by_reference {
221 let slot = build.unary(Opcode::IntToPtr, address, Type::PTR);
222 let held = build.load(Type::PTR, slot, info(8, 8), Flags::default());
223 address = build.unary(Opcode::PtrToInt, held, Type::int(64));
224 }
225
226 let args = func.push_values(&[address]);
227 let data = &mut func[inst];
228 data.opcode = Opcode::IntToPtr;
229 data.args = args;
230 data.extra = Extra::None;
231 data.flags = data.flags.intersection(Flags::legal_on(Opcode::IntToPtr));
232 func.append_inst(block, inst);
233 for at in rest {
234 func.append_inst(block, at);
235 }
236}
237
238fn cut(func: &mut Func, block: Block, inst: Inst) -> Vec<Inst> {
241 let rest: Vec<Inst> = func.insts(block).skip_while(|&at| at != inst).skip(1).collect();
242 func.remove_inst(inst);
243 for &at in &rest {
244 func.remove_inst(at);
245 }
246 rest
247}
248
249fn walk(
256 func: &mut Func,
257 block: Block,
258 inst: Inst,
259 list: Value,
260 wants: Wants,
261 found: impl FnOnce(&mut Builder<'_>, Value) -> Value,
262) -> (Block, Value) {
263 let span = func.span(inst);
264 let wide = Type::int(64);
265 let word = Type::int(32);
266 let (field, top, stride) =
267 if wants.float { (VR_OFFS, VR_TOP, VECTOR) } else { (GR_OFFS, GR_TOP, WORD) };
268 let fits = func.create_block();
269 let saved = func.create_block();
270 let stack = func.create_block();
271 let join = func.create_block();
272 let address = func.append_param(join, wide);
273
274 let mut build = Builder::new(func, block).at(span);
276 let counter = offset(&mut build, list, field);
277 let mut walked = build.load(word, counter, info(4, 4), Flags::default());
278 let zero = build.iconst(word, 0);
279 let out = build.icmp(IntPred::Sge, walked, zero);
280 build.br_if(out, stack, &[], fits, &[]);
281
282 let mut build = Builder::new(func, fits).at(span);
284 if wants.even {
285 let bump = build.iconst(word, 15);
286 walked = build.binary(Opcode::Add, walked, bump, Flags::default());
287 let mask = build.iconst(word, -16);
288 walked = build.binary(Opcode::And, walked, mask, Flags::default());
289 }
290 let by = build.iconst(word, i128::from(stride * wants.slots));
291 let stepped = build.binary(Opcode::Add, walked, by, Flags::default());
292 let counter = offset(&mut build, list, field);
293 build.store(stepped, counter, info(4, 4), Flags::default());
294 let zero = build.iconst(word, 0);
295 let past = build.icmp(IntPred::Sgt, stepped, zero);
296 build.br_if(past, stack, &[], saved, &[]);
297
298 let mut build = Builder::new(func, saved).at(span);
300 let at = offset(&mut build, list, top);
301 let top = build.load(Type::PTR, at, info(8, 8), Flags::default());
302 let back = build.unary(Opcode::SExt, walked, wide);
303 let here = added(&mut build, top, back);
304 let here = build.unary(Opcode::PtrToInt, here, wide);
305 let here = found(&mut build, here);
306 build.jump(join, &[here]);
307
308 let mut build = Builder::new(func, stack).at(span);
309 let there = in_memory(&mut build, list, wants);
310 build.jump(join, &[there]);
311
312 (join, address)
313}
314
315fn in_memory(build: &mut Builder<'_>, list: Value, wants: Wants) -> Value {
319 let wide = Type::int(64);
320 let pointer = offset(build, list, STACK);
321 let there = build.load(Type::PTR, pointer, info(8, 8), Flags::default());
322 let mut there = build.unary(Opcode::PtrToInt, there, wide);
323 if wants.wide {
324 let bump = build.iconst(wide, 15);
325 there = build.binary(Opcode::Add, there, bump, Flags::default());
326 let mask = build.iconst(wide, -16);
327 there = build.binary(Opcode::And, there, mask, Flags::default());
328 }
329 let by = build.iconst(wide, i128::from(wants.size.next_multiple_of(u64::from(WORD))));
330 let onward = build.binary(Opcode::Add, there, by, Flags::default());
331 let onward = build.unary(Opcode::IntToPtr, onward, Type::PTR);
332 build.store(onward, pointer, info(8, 8), Flags::default());
333 there
334}
335
336fn copied(build: &mut Builder<'_>, from: Value, slots: &[Slot], room: Value, align: u32) -> Value {
339 let from = build.unary(Opcode::IntToPtr, from, Type::PTR);
340 for (index, &slot) in slots.iter().enumerate() {
341 let bytes = width(slot);
342 let ty = if bytes > u64::from(WORD) {
343 Type::float(Float::F128)
344 } else {
345 Type::int(u32::try_from(bytes).unwrap_or(1) * 8)
346 };
347 let step = i64::from(VECTOR) * i64::try_from(index).unwrap_or(0);
348 let at = offset(build, from, step);
349 let aligned = u32::try_from(bytes).unwrap_or(1);
350 let value = build.load(ty, at, info(bytes, aligned), Flags::default());
351 let into = offset(build, room, i64::try_from(slot.offset()).unwrap_or(0));
352 let holds = info(bytes, super::part(align, slot.offset()));
353 build.store(value, into, holds, Flags::default());
354 }
355 build.unary(Opcode::PtrToInt, room, Type::int(64))
356}