1 //! Legalize instructions.
2 //!
3 //! A legal instruction is one that can be mapped directly to a machine code instruction for the
4 //! target ISA. The `legalize_function()` function takes as input any function and transforms it
5 //! into an equivalent function using only legal instructions.
6 //!
7 //! The characteristics of legal instructions depend on the target ISA, so any given instruction
8 //! can be legal for one ISA and illegal for another.
9 //!
10 //! Besides transforming instructions, the legalizer also fills out the `function.encodings` map
11 //! which provides a legal encoding recipe for every instruction.
12 //!
13 //! The legalizer does not deal with register allocation constraints. These constraints are derived
14 //! from the encoding recipes, and solved later by the register allocator.
15
16 use crate::bitset::BitSet;
17 use crate::cursor::{Cursor, FuncCursor};
18 use crate::flowgraph::ControlFlowGraph;
19 use crate::ir::types::{I32, I64};
20 use crate::ir::{self, InstBuilder, MemFlags};
21 use crate::isa::TargetIsa;
22 use crate::predicates;
23 use crate::timing;
24 use std::collections::BTreeSet;
25 use std::vec::Vec;
26
27 mod boundary;
28 mod call;
29 mod globalvalue;
30 mod heap;
31 mod libcall;
32 mod split;
33 mod table;
34
35 use self::call::expand_call;
36 use self::globalvalue::expand_global_value;
37 use self::heap::expand_heap_addr;
38 use self::libcall::expand_as_libcall;
39 use self::table::expand_table_addr;
40
41 enum LegalizeInstResult {
42 Done,
43 Legalized,
44 SplitLegalizePending,
45 }
46
47 /// Legalize `inst` for `isa`.
legalize_inst( inst: ir::Inst, pos: &mut FuncCursor, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa, ) -> LegalizeInstResult48 fn legalize_inst(
49 inst: ir::Inst,
50 pos: &mut FuncCursor,
51 cfg: &mut ControlFlowGraph,
52 isa: &dyn TargetIsa,
53 ) -> LegalizeInstResult {
54 let opcode = pos.func.dfg[inst].opcode();
55
56 // Check for ABI boundaries that need to be converted to the legalized signature.
57 if opcode.is_call() {
58 if boundary::handle_call_abi(inst, pos.func, cfg) {
59 return LegalizeInstResult::Legalized;
60 }
61 } else if opcode.is_return() {
62 if boundary::handle_return_abi(inst, pos.func, cfg) {
63 return LegalizeInstResult::Legalized;
64 }
65 } else if opcode.is_branch() {
66 split::simplify_branch_arguments(&mut pos.func.dfg, inst);
67 } else if opcode == ir::Opcode::Isplit {
68 pos.use_srcloc(inst);
69
70 let arg = match pos.func.dfg[inst] {
71 ir::InstructionData::Unary { arg, .. } => pos.func.dfg.resolve_aliases(arg),
72 _ => panic!("Expected isplit: {}", pos.func.dfg.display_inst(inst, None)),
73 };
74
75 match pos.func.dfg.value_def(arg) {
76 ir::ValueDef::Result(inst, _num) => {
77 if let ir::InstructionData::Binary {
78 opcode: ir::Opcode::Iconcat,
79 ..
80 } = pos.func.dfg[inst]
81 {
82 // `arg` was created by an `iconcat` instruction.
83 } else {
84 // `arg` was not created by an `iconcat` instruction. Don't try to resolve it,
85 // as otherwise `split::isplit` will re-insert the original `isplit`, causing
86 // an endless loop.
87 return LegalizeInstResult::SplitLegalizePending;
88 }
89 }
90 ir::ValueDef::Param(_ebb, _num) => {}
91 }
92
93 let res = pos.func.dfg.inst_results(inst).to_vec();
94 assert_eq!(res.len(), 2);
95 let (resl, resh) = (res[0], res[1]); // Prevent borrowck error
96
97 // Remove old isplit
98 pos.func.dfg.clear_results(inst);
99 pos.remove_inst();
100
101 let curpos = pos.position();
102 let srcloc = pos.srcloc();
103 let (xl, xh) = split::isplit(pos.func, cfg, curpos, srcloc, arg);
104
105 pos.func.dfg.change_to_alias(resl, xl);
106 pos.func.dfg.change_to_alias(resh, xh);
107
108 return LegalizeInstResult::Legalized;
109 }
110
111 match pos.func.update_encoding(inst, isa) {
112 Ok(()) => LegalizeInstResult::Done,
113 Err(action) => {
114 // We should transform the instruction into legal equivalents.
115 // If the current instruction was replaced, we need to double back and revisit
116 // the expanded sequence. This is both to assign encodings and possible to
117 // expand further.
118 // There's a risk of infinite looping here if the legalization patterns are
119 // unsound. Should we attempt to detect that?
120 if action(inst, pos.func, cfg, isa) {
121 return LegalizeInstResult::Legalized;
122 }
123
124 // We don't have any pattern expansion for this instruction either.
125 // Try converting it to a library call as a last resort.
126 if expand_as_libcall(inst, pos.func, isa) {
127 LegalizeInstResult::Legalized
128 } else {
129 LegalizeInstResult::Done
130 }
131 }
132 }
133 }
134
135 /// Legalize `func` for `isa`.
136 ///
137 /// - Transform any instructions that don't have a legal representation in `isa`.
138 /// - Fill out `func.encodings`.
139 ///
legalize_function(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa)140 pub fn legalize_function(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa) {
141 let _tt = timing::legalize();
142 debug_assert!(cfg.is_valid());
143
144 boundary::legalize_signatures(func, isa);
145
146 func.encodings.resize(func.dfg.num_insts());
147
148 let mut pos = FuncCursor::new(func);
149
150 // This must be a set to prevent trying to legalize `isplit` and `vsplit` twice in certain cases.
151 let mut pending_splits = BTreeSet::new();
152
153 // Process EBBs in layout order. Some legalization actions may split the current EBB or append
154 // new ones to the end. We need to make sure we visit those new EBBs too.
155 while let Some(ebb) = pos.next_ebb() {
156 split::split_ebb_params(pos.func, cfg, ebb);
157
158 // Keep track of the cursor position before the instruction being processed, so we can
159 // double back when replacing instructions.
160 let mut prev_pos = pos.position();
161
162 while let Some(inst) = pos.next_inst() {
163 match legalize_inst(inst, &mut pos, cfg, isa) {
164 // Remember this position in case we need to double back.
165 LegalizeInstResult::Done => prev_pos = pos.position(),
166
167 // Go back and legalize the inserted return value conversion instructions.
168 LegalizeInstResult::Legalized => pos.set_position(prev_pos),
169
170 // The argument of a `isplit` or `vsplit` instruction didn't resolve to a
171 // `iconcat` or `vconcat` instruction. Try again after legalizing the rest of
172 // the instructions.
173 LegalizeInstResult::SplitLegalizePending => {
174 pending_splits.insert(inst);
175 }
176 }
177 }
178 }
179
180 // Try legalizing `isplit` and `vsplit` instructions, which could not previously be legalized.
181 for inst in pending_splits {
182 pos.goto_inst(inst);
183 legalize_inst(inst, &mut pos, cfg, isa);
184 }
185
186 // Now that we've lowered all br_tables, we don't need the jump tables anymore.
187 if !isa.flags().jump_tables_enabled() {
188 pos.func.jump_tables.clear();
189 }
190 }
191
192 // Include legalization patterns that were generated by `gen_legalizer.rs` from the
193 // `TransformGroup` in `cranelift-codegen/meta/shared/legalize.rs`.
194 //
195 // Concretely, this defines private functions `narrow()`, and `expand()`.
196 include!(concat!(env!("OUT_DIR"), "/legalizer.rs"));
197
198 /// Custom expansion for conditional trap instructions.
199 /// TODO: Add CFG support to the Rust DSL patterns so we won't have to do this.
expand_cond_trap( inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowGraph, _isa: &dyn TargetIsa, )200 fn expand_cond_trap(
201 inst: ir::Inst,
202 func: &mut ir::Function,
203 cfg: &mut ControlFlowGraph,
204 _isa: &dyn TargetIsa,
205 ) {
206 // Parse the instruction.
207 let trapz;
208 let (arg, code) = match func.dfg[inst] {
209 ir::InstructionData::CondTrap { opcode, arg, code } => {
210 // We want to branch *over* an unconditional trap.
211 trapz = match opcode {
212 ir::Opcode::Trapz => true,
213 ir::Opcode::Trapnz => false,
214 _ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)),
215 };
216 (arg, code)
217 }
218 _ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)),
219 };
220
221 // Split the EBB after `inst`:
222 //
223 // trapnz arg
224 // ..
225 //
226 // Becomes:
227 //
228 // brz arg, new_ebb_resume
229 // jump new_ebb_trap
230 //
231 // new_ebb_trap:
232 // trap
233 //
234 // new_ebb_resume:
235 // ..
236 let old_ebb = func.layout.pp_ebb(inst);
237 let new_ebb_trap = func.dfg.make_ebb();
238 let new_ebb_resume = func.dfg.make_ebb();
239
240 // Replace trap instruction by the inverted condition.
241 if trapz {
242 func.dfg.replace(inst).brnz(arg, new_ebb_resume, &[]);
243 } else {
244 func.dfg.replace(inst).brz(arg, new_ebb_resume, &[]);
245 }
246
247 // Add jump instruction after the inverted branch.
248 let mut pos = FuncCursor::new(func).after_inst(inst);
249 pos.use_srcloc(inst);
250 pos.ins().jump(new_ebb_trap, &[]);
251
252 // Insert the new label and the unconditional trap terminator.
253 pos.insert_ebb(new_ebb_trap);
254 pos.ins().trap(code);
255
256 // Insert the new label and resume the execution when the trap fails.
257 pos.insert_ebb(new_ebb_resume);
258
259 // Finally update the CFG.
260 cfg.recompute_ebb(pos.func, old_ebb);
261 cfg.recompute_ebb(pos.func, new_ebb_resume);
262 cfg.recompute_ebb(pos.func, new_ebb_trap);
263 }
264
265 /// Jump tables.
expand_br_table( inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa, )266 fn expand_br_table(
267 inst: ir::Inst,
268 func: &mut ir::Function,
269 cfg: &mut ControlFlowGraph,
270 isa: &dyn TargetIsa,
271 ) {
272 if isa.flags().jump_tables_enabled() {
273 expand_br_table_jt(inst, func, cfg, isa);
274 } else {
275 expand_br_table_conds(inst, func, cfg, isa);
276 }
277 }
278
279 /// Expand br_table to jump table.
expand_br_table_jt( inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa, )280 fn expand_br_table_jt(
281 inst: ir::Inst,
282 func: &mut ir::Function,
283 cfg: &mut ControlFlowGraph,
284 isa: &dyn TargetIsa,
285 ) {
286 use crate::ir::condcodes::IntCC;
287
288 let (arg, default_ebb, table) = match func.dfg[inst] {
289 ir::InstructionData::BranchTable {
290 opcode: ir::Opcode::BrTable,
291 arg,
292 destination,
293 table,
294 } => (arg, destination, table),
295 _ => panic!("Expected br_table: {}", func.dfg.display_inst(inst, None)),
296 };
297
298 // Rewrite:
299 //
300 // br_table $idx, default_ebb, $jt
301 //
302 // To:
303 //
304 // $oob = ifcmp_imm $idx, len($jt)
305 // brif uge $oob, default_ebb
306 // jump fallthrough_ebb
307 //
308 // fallthrough_ebb:
309 // $base = jump_table_base.i64 $jt
310 // $rel_addr = jump_table_entry.i64 $idx, $base, 4, $jt
311 // $addr = iadd $base, $rel_addr
312 // indirect_jump_table_br $addr, $jt
313
314 let ebb = func.layout.pp_ebb(inst);
315 let jump_table_ebb = func.dfg.make_ebb();
316
317 let mut pos = FuncCursor::new(func).at_inst(inst);
318 pos.use_srcloc(inst);
319
320 // Bounds check.
321 let table_size = pos.func.jump_tables[table].len() as i64;
322 let oob = pos
323 .ins()
324 .icmp_imm(IntCC::UnsignedGreaterThanOrEqual, arg, table_size);
325
326 pos.ins().brnz(oob, default_ebb, &[]);
327 pos.ins().jump(jump_table_ebb, &[]);
328 pos.insert_ebb(jump_table_ebb);
329
330 let addr_ty = isa.pointer_type();
331
332 let arg = if pos.func.dfg.value_type(arg) == addr_ty {
333 arg
334 } else {
335 pos.ins().uextend(addr_ty, arg)
336 };
337
338 let base_addr = pos.ins().jump_table_base(addr_ty, table);
339 let entry = pos
340 .ins()
341 .jump_table_entry(arg, base_addr, I32.bytes() as u8, table);
342
343 let addr = pos.ins().iadd(base_addr, entry);
344 pos.ins().indirect_jump_table_br(addr, table);
345
346 pos.remove_inst();
347 cfg.recompute_ebb(pos.func, ebb);
348 cfg.recompute_ebb(pos.func, jump_table_ebb);
349 }
350
351 /// Expand br_table to series of conditionals.
expand_br_table_conds( inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowGraph, _isa: &dyn TargetIsa, )352 fn expand_br_table_conds(
353 inst: ir::Inst,
354 func: &mut ir::Function,
355 cfg: &mut ControlFlowGraph,
356 _isa: &dyn TargetIsa,
357 ) {
358 use crate::ir::condcodes::IntCC;
359
360 let (arg, default_ebb, table) = match func.dfg[inst] {
361 ir::InstructionData::BranchTable {
362 opcode: ir::Opcode::BrTable,
363 arg,
364 destination,
365 table,
366 } => (arg, destination, table),
367 _ => panic!("Expected br_table: {}", func.dfg.display_inst(inst, None)),
368 };
369
370 let ebb = func.layout.pp_ebb(inst);
371
372 // This is a poor man's jump table using just a sequence of conditional branches.
373 let table_size = func.jump_tables[table].len();
374 let mut cond_failed_ebb = vec![];
375 if table_size >= 1 {
376 cond_failed_ebb = std::vec::Vec::with_capacity(table_size - 1);
377 for _ in 0..table_size - 1 {
378 cond_failed_ebb.push(func.dfg.make_ebb());
379 }
380 }
381
382 let mut pos = FuncCursor::new(func).at_inst(inst);
383 pos.use_srcloc(inst);
384
385 for i in 0..table_size {
386 let dest = pos.func.jump_tables[table].as_slice()[i];
387 let t = pos.ins().icmp_imm(IntCC::Equal, arg, i as i64);
388 pos.ins().brnz(t, dest, &[]);
389 // Jump to the next case.
390 if i < table_size - 1 {
391 pos.ins().jump(cond_failed_ebb[i], &[]);
392 pos.insert_ebb(cond_failed_ebb[i]);
393 }
394 }
395
396 // `br_table` jumps to the default destination if nothing matches
397 pos.ins().jump(default_ebb, &[]);
398
399 pos.remove_inst();
400 cfg.recompute_ebb(pos.func, ebb);
401 for failed_ebb in cond_failed_ebb.into_iter() {
402 cfg.recompute_ebb(pos.func, failed_ebb);
403 }
404 }
405
406 /// Expand the select instruction.
407 ///
408 /// Conditional moves are available in some ISAs for some register classes. The remaining selects
409 /// are handled by a branch.
expand_select( inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowGraph, _isa: &dyn TargetIsa, )410 fn expand_select(
411 inst: ir::Inst,
412 func: &mut ir::Function,
413 cfg: &mut ControlFlowGraph,
414 _isa: &dyn TargetIsa,
415 ) {
416 let (ctrl, tval, fval) = match func.dfg[inst] {
417 ir::InstructionData::Ternary {
418 opcode: ir::Opcode::Select,
419 args,
420 } => (args[0], args[1], args[2]),
421 _ => panic!("Expected select: {}", func.dfg.display_inst(inst, None)),
422 };
423
424 // Replace `result = select ctrl, tval, fval` with:
425 //
426 // brnz ctrl, new_ebb(tval)
427 // jump new_ebb(fval)
428 // new_ebb(result):
429 let old_ebb = func.layout.pp_ebb(inst);
430 let result = func.dfg.first_result(inst);
431 func.dfg.clear_results(inst);
432 let new_ebb = func.dfg.make_ebb();
433 func.dfg.attach_ebb_param(new_ebb, result);
434
435 func.dfg.replace(inst).brnz(ctrl, new_ebb, &[tval]);
436 let mut pos = FuncCursor::new(func).after_inst(inst);
437 pos.use_srcloc(inst);
438 pos.ins().jump(new_ebb, &[fval]);
439 pos.insert_ebb(new_ebb);
440
441 cfg.recompute_ebb(pos.func, new_ebb);
442 cfg.recompute_ebb(pos.func, old_ebb);
443 }
444
expand_br_icmp( inst: ir::Inst, func: &mut ir::Function, cfg: &mut ControlFlowGraph, _isa: &dyn TargetIsa, )445 fn expand_br_icmp(
446 inst: ir::Inst,
447 func: &mut ir::Function,
448 cfg: &mut ControlFlowGraph,
449 _isa: &dyn TargetIsa,
450 ) {
451 let (cond, a, b, destination, ebb_args) = match func.dfg[inst] {
452 ir::InstructionData::BranchIcmp {
453 cond,
454 destination,
455 ref args,
456 ..
457 } => (
458 cond,
459 args.get(0, &func.dfg.value_lists).unwrap(),
460 args.get(1, &func.dfg.value_lists).unwrap(),
461 destination,
462 args.as_slice(&func.dfg.value_lists)[2..].to_vec(),
463 ),
464 _ => panic!("Expected br_icmp {}", func.dfg.display_inst(inst, None)),
465 };
466
467 let old_ebb = func.layout.pp_ebb(inst);
468 func.dfg.clear_results(inst);
469
470 let icmp_res = func.dfg.replace(inst).icmp(cond, a, b);
471 let mut pos = FuncCursor::new(func).after_inst(inst);
472 pos.use_srcloc(inst);
473 pos.ins().brnz(icmp_res, destination, &ebb_args);
474
475 cfg.recompute_ebb(pos.func, destination);
476 cfg.recompute_ebb(pos.func, old_ebb);
477 }
478
479 /// Expand illegal `f32const` and `f64const` instructions.
expand_fconst( inst: ir::Inst, func: &mut ir::Function, _cfg: &mut ControlFlowGraph, _isa: &dyn TargetIsa, )480 fn expand_fconst(
481 inst: ir::Inst,
482 func: &mut ir::Function,
483 _cfg: &mut ControlFlowGraph,
484 _isa: &dyn TargetIsa,
485 ) {
486 let ty = func.dfg.value_type(func.dfg.first_result(inst));
487 debug_assert!(!ty.is_vector(), "Only scalar fconst supported: {}", ty);
488
489 // In the future, we may want to generate constant pool entries for these constants, but for
490 // now use an `iconst` and a bit cast.
491 let mut pos = FuncCursor::new(func).at_inst(inst);
492 pos.use_srcloc(inst);
493 let ival = match pos.func.dfg[inst] {
494 ir::InstructionData::UnaryIeee32 {
495 opcode: ir::Opcode::F32const,
496 imm,
497 } => pos.ins().iconst(ir::types::I32, i64::from(imm.bits())),
498 ir::InstructionData::UnaryIeee64 {
499 opcode: ir::Opcode::F64const,
500 imm,
501 } => pos.ins().iconst(ir::types::I64, imm.bits() as i64),
502 _ => panic!("Expected fconst: {}", pos.func.dfg.display_inst(inst, None)),
503 };
504 pos.func.dfg.replace(inst).bitcast(ty, ival);
505 }
506
507 /// Expand illegal `stack_load` instructions.
expand_stack_load( inst: ir::Inst, func: &mut ir::Function, _cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa, )508 fn expand_stack_load(
509 inst: ir::Inst,
510 func: &mut ir::Function,
511 _cfg: &mut ControlFlowGraph,
512 isa: &dyn TargetIsa,
513 ) {
514 let ty = func.dfg.value_type(func.dfg.first_result(inst));
515 let addr_ty = isa.pointer_type();
516
517 let mut pos = FuncCursor::new(func).at_inst(inst);
518 pos.use_srcloc(inst);
519
520 let (stack_slot, offset) = match pos.func.dfg[inst] {
521 ir::InstructionData::StackLoad {
522 opcode: _opcode,
523 stack_slot,
524 offset,
525 } => (stack_slot, offset),
526 _ => panic!(
527 "Expected stack_load: {}",
528 pos.func.dfg.display_inst(inst, None)
529 ),
530 };
531
532 let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);
533
534 // Stack slots are required to be accessible and aligned.
535 let mflags = MemFlags::trusted();
536 pos.func.dfg.replace(inst).load(ty, mflags, addr, 0);
537 }
538
539 /// Expand illegal `stack_store` instructions.
expand_stack_store( inst: ir::Inst, func: &mut ir::Function, _cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa, )540 fn expand_stack_store(
541 inst: ir::Inst,
542 func: &mut ir::Function,
543 _cfg: &mut ControlFlowGraph,
544 isa: &dyn TargetIsa,
545 ) {
546 let addr_ty = isa.pointer_type();
547
548 let mut pos = FuncCursor::new(func).at_inst(inst);
549 pos.use_srcloc(inst);
550
551 let (val, stack_slot, offset) = match pos.func.dfg[inst] {
552 ir::InstructionData::StackStore {
553 opcode: _opcode,
554 arg,
555 stack_slot,
556 offset,
557 } => (arg, stack_slot, offset),
558 _ => panic!(
559 "Expected stack_store: {}",
560 pos.func.dfg.display_inst(inst, None)
561 ),
562 };
563
564 let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);
565
566 let mut mflags = MemFlags::new();
567 // Stack slots are required to be accessible and aligned.
568 mflags.set_notrap();
569 mflags.set_aligned();
570 pos.func.dfg.replace(inst).store(mflags, val, addr, 0);
571 }
572
573 /// Split a load into two parts before `iconcat`ing the result together.
narrow_load( inst: ir::Inst, func: &mut ir::Function, _cfg: &mut ControlFlowGraph, _isa: &dyn TargetIsa, )574 fn narrow_load(
575 inst: ir::Inst,
576 func: &mut ir::Function,
577 _cfg: &mut ControlFlowGraph,
578 _isa: &dyn TargetIsa,
579 ) {
580 let mut pos = FuncCursor::new(func).at_inst(inst);
581 pos.use_srcloc(inst);
582
583 let (ptr, offset, flags) = match pos.func.dfg[inst] {
584 ir::InstructionData::Load {
585 opcode: ir::Opcode::Load,
586 arg,
587 offset,
588 flags,
589 } => (arg, offset, flags),
590 _ => panic!("Expected load: {}", pos.func.dfg.display_inst(inst, None)),
591 };
592
593 let res_ty = pos.func.dfg.ctrl_typevar(inst);
594 let small_ty = res_ty.half_width().expect("Can't narrow load");
595
596 let al = pos.ins().load(small_ty, flags, ptr, offset);
597 let ah = pos.ins().load(
598 small_ty,
599 flags,
600 ptr,
601 offset.try_add_i64(8).expect("load offset overflow"),
602 );
603 pos.func.dfg.replace(inst).iconcat(al, ah);
604 }
605
606 /// Split a store into two parts after `isplit`ing the value.
narrow_store( inst: ir::Inst, func: &mut ir::Function, _cfg: &mut ControlFlowGraph, _isa: &dyn TargetIsa, )607 fn narrow_store(
608 inst: ir::Inst,
609 func: &mut ir::Function,
610 _cfg: &mut ControlFlowGraph,
611 _isa: &dyn TargetIsa,
612 ) {
613 let mut pos = FuncCursor::new(func).at_inst(inst);
614 pos.use_srcloc(inst);
615
616 let (val, ptr, offset, flags) = match pos.func.dfg[inst] {
617 ir::InstructionData::Store {
618 opcode: ir::Opcode::Store,
619 args,
620 offset,
621 flags,
622 } => (args[0], args[1], offset, flags),
623 _ => panic!("Expected store: {}", pos.func.dfg.display_inst(inst, None)),
624 };
625
626 let (al, ah) = pos.ins().isplit(val);
627 pos.ins().store(flags, al, ptr, offset);
628 pos.ins().store(
629 flags,
630 ah,
631 ptr,
632 offset.try_add_i64(8).expect("store offset overflow"),
633 );
634 pos.remove_inst();
635 }
636
637 /// Expands an illegal iconst value by splitting it into two.
narrow_iconst( inst: ir::Inst, func: &mut ir::Function, _cfg: &mut ControlFlowGraph, isa: &dyn TargetIsa, )638 fn narrow_iconst(
639 inst: ir::Inst,
640 func: &mut ir::Function,
641 _cfg: &mut ControlFlowGraph,
642 isa: &dyn TargetIsa,
643 ) {
644 let imm: i64 = if let ir::InstructionData::UnaryImm {
645 opcode: ir::Opcode::Iconst,
646 imm,
647 } = &func.dfg[inst]
648 {
649 (*imm).into()
650 } else {
651 panic!("unexpected instruction in narrow_iconst");
652 };
653
654 let mut pos = FuncCursor::new(func).at_inst(inst);
655 pos.use_srcloc(inst);
656
657 let ty = pos.func.dfg.ctrl_typevar(inst);
658 if isa.pointer_bits() == 32 && ty == I64 {
659 let low = pos.ins().iconst(I32, imm & 0xffffffff);
660 let high = pos.ins().iconst(I32, imm >> 32);
661 // The instruction has as many results as iconcat, so no need to replace them.
662 pos.func.dfg.replace(inst).iconcat(low, high);
663 return;
664 }
665
666 unimplemented!("missing encoding or legalization for iconst.{:?}", ty);
667 }
668