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