Skip to main content

cranelift_codegen/legalizer/
mod.rs

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
16use crate::bitset::BitSet;
17use crate::cursor::{Cursor, FuncCursor};
18use crate::flowgraph::ControlFlowGraph;
19use crate::ir::types::{I32, I64};
20use crate::ir::{self, InstBuilder, MemFlags};
21use crate::isa::TargetIsa;
22use crate::predicates;
23use crate::timing;
24use alloc::collections::BTreeSet;
25use alloc::vec::Vec;
26
27mod boundary;
28mod call;
29mod globalvalue;
30mod heap;
31mod libcall;
32mod split;
33mod table;
34
35use self::call::expand_call;
36use self::globalvalue::expand_global_value;
37use self::heap::expand_heap_addr;
38use self::libcall::expand_as_libcall;
39use self::table::expand_table_addr;
40
41enum LegalizeInstResult {
42    Done,
43    Legalized,
44    SplitLegalizePending,
45}
46
47/// Legalize `inst` for `isa`.
48fn 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(isa, 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///
140pub 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    let func_begin = pos.position();
150
151    // Split ebb params before trying to legalize instructions, so that the newly introduced
152    // isplit instructions get legalized.
153    while let Some(ebb) = pos.next_ebb() {
154        split::split_ebb_params(pos.func, cfg, ebb);
155    }
156
157    pos.set_position(func_begin);
158
159    // This must be a set to prevent trying to legalize `isplit` and `vsplit` twice in certain cases.
160    let mut pending_splits = BTreeSet::new();
161
162    // Process EBBs in layout order. Some legalization actions may split the current EBB or append
163    // new ones to the end. We need to make sure we visit those new EBBs too.
164    while let Some(_ebb) = pos.next_ebb() {
165        // Keep track of the cursor position before the instruction being processed, so we can
166        // double back when replacing instructions.
167        let mut prev_pos = pos.position();
168
169        while let Some(inst) = pos.next_inst() {
170            match legalize_inst(inst, &mut pos, cfg, isa) {
171                // Remember this position in case we need to double back.
172                LegalizeInstResult::Done => prev_pos = pos.position(),
173
174                // Go back and legalize the inserted return value conversion instructions.
175                LegalizeInstResult::Legalized => pos.set_position(prev_pos),
176
177                // The argument of a `isplit` or `vsplit` instruction didn't resolve to a
178                // `iconcat` or `vconcat` instruction. Try again after legalizing the rest of
179                // the instructions.
180                LegalizeInstResult::SplitLegalizePending => {
181                    pending_splits.insert(inst);
182                }
183            }
184        }
185    }
186
187    // Try legalizing `isplit` and `vsplit` instructions, which could not previously be legalized.
188    for inst in pending_splits {
189        pos.goto_inst(inst);
190        legalize_inst(inst, &mut pos, cfg, isa);
191    }
192
193    // Now that we've lowered all br_tables, we don't need the jump tables anymore.
194    if !isa.flags().jump_tables_enabled() {
195        pos.func.jump_tables.clear();
196    }
197}
198
199// Include legalization patterns that were generated by `gen_legalizer.rs` from the
200// `TransformGroup` in `cranelift-codegen/meta/shared/legalize.rs`.
201//
202// Concretely, this defines private functions `narrow()`, and `expand()`.
203include!(concat!(env!("OUT_DIR"), "/legalizer.rs"));
204
205/// Custom expansion for conditional trap instructions.
206/// TODO: Add CFG support to the Rust DSL patterns so we won't have to do this.
207fn expand_cond_trap(
208    inst: ir::Inst,
209    func: &mut ir::Function,
210    cfg: &mut ControlFlowGraph,
211    _isa: &dyn TargetIsa,
212) {
213    // Parse the instruction.
214    let trapz;
215    let (arg, code) = match func.dfg[inst] {
216        ir::InstructionData::CondTrap { opcode, arg, code } => {
217            // We want to branch *over* an unconditional trap.
218            trapz = match opcode {
219                ir::Opcode::Trapz => true,
220                ir::Opcode::Trapnz => false,
221                _ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)),
222            };
223            (arg, code)
224        }
225        _ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)),
226    };
227
228    // Split the EBB after `inst`:
229    //
230    //     trapnz arg
231    //     ..
232    //
233    // Becomes:
234    //
235    //     brz arg, new_ebb_resume
236    //     jump new_ebb_trap
237    //
238    //   new_ebb_trap:
239    //     trap
240    //
241    //   new_ebb_resume:
242    //     ..
243    let old_ebb = func.layout.pp_ebb(inst);
244    let new_ebb_trap = func.dfg.make_ebb();
245    let new_ebb_resume = func.dfg.make_ebb();
246
247    // Replace trap instruction by the inverted condition.
248    if trapz {
249        func.dfg.replace(inst).brnz(arg, new_ebb_resume, &[]);
250    } else {
251        func.dfg.replace(inst).brz(arg, new_ebb_resume, &[]);
252    }
253
254    // Add jump instruction after the inverted branch.
255    let mut pos = FuncCursor::new(func).after_inst(inst);
256    pos.use_srcloc(inst);
257    pos.ins().jump(new_ebb_trap, &[]);
258
259    // Insert the new label and the unconditional trap terminator.
260    pos.insert_ebb(new_ebb_trap);
261    pos.ins().trap(code);
262
263    // Insert the new label and resume the execution when the trap fails.
264    pos.insert_ebb(new_ebb_resume);
265
266    // Finally update the CFG.
267    cfg.recompute_ebb(pos.func, old_ebb);
268    cfg.recompute_ebb(pos.func, new_ebb_resume);
269    cfg.recompute_ebb(pos.func, new_ebb_trap);
270}
271
272/// Jump tables.
273fn expand_br_table(
274    inst: ir::Inst,
275    func: &mut ir::Function,
276    cfg: &mut ControlFlowGraph,
277    isa: &dyn TargetIsa,
278) {
279    if isa.flags().jump_tables_enabled() {
280        expand_br_table_jt(inst, func, cfg, isa);
281    } else {
282        expand_br_table_conds(inst, func, cfg, isa);
283    }
284}
285
286/// Expand br_table to jump table.
287fn expand_br_table_jt(
288    inst: ir::Inst,
289    func: &mut ir::Function,
290    cfg: &mut ControlFlowGraph,
291    isa: &dyn TargetIsa,
292) {
293    use crate::ir::condcodes::IntCC;
294
295    let (arg, default_ebb, table) = match func.dfg[inst] {
296        ir::InstructionData::BranchTable {
297            opcode: ir::Opcode::BrTable,
298            arg,
299            destination,
300            table,
301        } => (arg, destination, table),
302        _ => panic!("Expected br_table: {}", func.dfg.display_inst(inst, None)),
303    };
304
305    // Rewrite:
306    //
307    //     br_table $idx, default_ebb, $jt
308    //
309    // To:
310    //
311    //     $oob = ifcmp_imm $idx, len($jt)
312    //     brif uge $oob, default_ebb
313    //     jump fallthrough_ebb
314    //
315    //   fallthrough_ebb:
316    //     $base = jump_table_base.i64 $jt
317    //     $rel_addr = jump_table_entry.i64 $idx, $base, 4, $jt
318    //     $addr = iadd $base, $rel_addr
319    //     indirect_jump_table_br $addr, $jt
320
321    let ebb = func.layout.pp_ebb(inst);
322    let jump_table_ebb = func.dfg.make_ebb();
323
324    let mut pos = FuncCursor::new(func).at_inst(inst);
325    pos.use_srcloc(inst);
326
327    // Bounds check.
328    let table_size = pos.func.jump_tables[table].len() as i64;
329    let oob = pos
330        .ins()
331        .icmp_imm(IntCC::UnsignedGreaterThanOrEqual, arg, table_size);
332
333    pos.ins().brnz(oob, default_ebb, &[]);
334    pos.ins().jump(jump_table_ebb, &[]);
335    pos.insert_ebb(jump_table_ebb);
336
337    let addr_ty = isa.pointer_type();
338
339    let arg = if pos.func.dfg.value_type(arg) == addr_ty {
340        arg
341    } else {
342        pos.ins().uextend(addr_ty, arg)
343    };
344
345    let base_addr = pos.ins().jump_table_base(addr_ty, table);
346    let entry = pos
347        .ins()
348        .jump_table_entry(arg, base_addr, I32.bytes() as u8, table);
349
350    let addr = pos.ins().iadd(base_addr, entry);
351    pos.ins().indirect_jump_table_br(addr, table);
352
353    pos.remove_inst();
354    cfg.recompute_ebb(pos.func, ebb);
355    cfg.recompute_ebb(pos.func, jump_table_ebb);
356}
357
358/// Expand br_table to series of conditionals.
359fn expand_br_table_conds(
360    inst: ir::Inst,
361    func: &mut ir::Function,
362    cfg: &mut ControlFlowGraph,
363    _isa: &dyn TargetIsa,
364) {
365    use crate::ir::condcodes::IntCC;
366
367    let (arg, default_ebb, table) = match func.dfg[inst] {
368        ir::InstructionData::BranchTable {
369            opcode: ir::Opcode::BrTable,
370            arg,
371            destination,
372            table,
373        } => (arg, destination, table),
374        _ => panic!("Expected br_table: {}", func.dfg.display_inst(inst, None)),
375    };
376
377    let ebb = func.layout.pp_ebb(inst);
378
379    // This is a poor man's jump table using just a sequence of conditional branches.
380    let table_size = func.jump_tables[table].len();
381    let mut cond_failed_ebb = vec![];
382    if table_size >= 1 {
383        cond_failed_ebb = alloc::vec::Vec::with_capacity(table_size - 1);
384        for _ in 0..table_size - 1 {
385            cond_failed_ebb.push(func.dfg.make_ebb());
386        }
387    }
388
389    let mut pos = FuncCursor::new(func).at_inst(inst);
390    pos.use_srcloc(inst);
391
392    // Ignore the lint for this loop as the range needs to be 0 to table_size
393    #[allow(clippy::needless_range_loop)]
394    for i in 0..table_size {
395        let dest = pos.func.jump_tables[table].as_slice()[i];
396        let t = pos.ins().icmp_imm(IntCC::Equal, arg, i as i64);
397        pos.ins().brnz(t, dest, &[]);
398        // Jump to the next case.
399        if i < table_size - 1 {
400            let ebb = cond_failed_ebb[i];
401            pos.ins().jump(ebb, &[]);
402            pos.insert_ebb(ebb);
403        }
404    }
405
406    // `br_table` jumps to the default destination if nothing matches
407    pos.ins().jump(default_ebb, &[]);
408
409    pos.remove_inst();
410    cfg.recompute_ebb(pos.func, ebb);
411    for failed_ebb in cond_failed_ebb.into_iter() {
412        cfg.recompute_ebb(pos.func, failed_ebb);
413    }
414}
415
416/// Expand the select instruction.
417///
418/// Conditional moves are available in some ISAs for some register classes. The remaining selects
419/// are handled by a branch.
420fn expand_select(
421    inst: ir::Inst,
422    func: &mut ir::Function,
423    cfg: &mut ControlFlowGraph,
424    _isa: &dyn TargetIsa,
425) {
426    let (ctrl, tval, fval) = match func.dfg[inst] {
427        ir::InstructionData::Ternary {
428            opcode: ir::Opcode::Select,
429            args,
430        } => (args[0], args[1], args[2]),
431        _ => panic!("Expected select: {}", func.dfg.display_inst(inst, None)),
432    };
433
434    // Replace `result = select ctrl, tval, fval` with:
435    //
436    //   brnz ctrl, new_ebb(tval)
437    //   jump new_ebb(fval)
438    // new_ebb(result):
439    let old_ebb = func.layout.pp_ebb(inst);
440    let result = func.dfg.first_result(inst);
441    func.dfg.clear_results(inst);
442    let new_ebb = func.dfg.make_ebb();
443    func.dfg.attach_ebb_param(new_ebb, result);
444
445    func.dfg.replace(inst).brnz(ctrl, new_ebb, &[tval]);
446    let mut pos = FuncCursor::new(func).after_inst(inst);
447    pos.use_srcloc(inst);
448    pos.ins().jump(new_ebb, &[fval]);
449    pos.insert_ebb(new_ebb);
450
451    cfg.recompute_ebb(pos.func, new_ebb);
452    cfg.recompute_ebb(pos.func, old_ebb);
453}
454
455fn expand_br_icmp(
456    inst: ir::Inst,
457    func: &mut ir::Function,
458    cfg: &mut ControlFlowGraph,
459    _isa: &dyn TargetIsa,
460) {
461    let (cond, a, b, destination, ebb_args) = match func.dfg[inst] {
462        ir::InstructionData::BranchIcmp {
463            cond,
464            destination,
465            ref args,
466            ..
467        } => (
468            cond,
469            args.get(0, &func.dfg.value_lists).unwrap(),
470            args.get(1, &func.dfg.value_lists).unwrap(),
471            destination,
472            args.as_slice(&func.dfg.value_lists)[2..].to_vec(),
473        ),
474        _ => panic!("Expected br_icmp {}", func.dfg.display_inst(inst, None)),
475    };
476
477    let old_ebb = func.layout.pp_ebb(inst);
478    func.dfg.clear_results(inst);
479
480    let icmp_res = func.dfg.replace(inst).icmp(cond, a, b);
481    let mut pos = FuncCursor::new(func).after_inst(inst);
482    pos.use_srcloc(inst);
483    pos.ins().brnz(icmp_res, destination, &ebb_args);
484
485    cfg.recompute_ebb(pos.func, destination);
486    cfg.recompute_ebb(pos.func, old_ebb);
487}
488
489/// Expand illegal `f32const` and `f64const` instructions.
490fn expand_fconst(
491    inst: ir::Inst,
492    func: &mut ir::Function,
493    _cfg: &mut ControlFlowGraph,
494    _isa: &dyn TargetIsa,
495) {
496    let ty = func.dfg.value_type(func.dfg.first_result(inst));
497    debug_assert!(!ty.is_vector(), "Only scalar fconst supported: {}", ty);
498
499    // In the future, we may want to generate constant pool entries for these constants, but for
500    // now use an `iconst` and a bit cast.
501    let mut pos = FuncCursor::new(func).at_inst(inst);
502    pos.use_srcloc(inst);
503    let ival = match pos.func.dfg[inst] {
504        ir::InstructionData::UnaryIeee32 {
505            opcode: ir::Opcode::F32const,
506            imm,
507        } => pos.ins().iconst(ir::types::I32, i64::from(imm.bits())),
508        ir::InstructionData::UnaryIeee64 {
509            opcode: ir::Opcode::F64const,
510            imm,
511        } => pos.ins().iconst(ir::types::I64, imm.bits() as i64),
512        _ => panic!("Expected fconst: {}", pos.func.dfg.display_inst(inst, None)),
513    };
514    pos.func.dfg.replace(inst).bitcast(ty, ival);
515}
516
517/// Expand illegal `stack_load` instructions.
518fn expand_stack_load(
519    inst: ir::Inst,
520    func: &mut ir::Function,
521    _cfg: &mut ControlFlowGraph,
522    isa: &dyn TargetIsa,
523) {
524    let ty = func.dfg.value_type(func.dfg.first_result(inst));
525    let addr_ty = isa.pointer_type();
526
527    let mut pos = FuncCursor::new(func).at_inst(inst);
528    pos.use_srcloc(inst);
529
530    let (stack_slot, offset) = match pos.func.dfg[inst] {
531        ir::InstructionData::StackLoad {
532            opcode: _opcode,
533            stack_slot,
534            offset,
535        } => (stack_slot, offset),
536        _ => panic!(
537            "Expected stack_load: {}",
538            pos.func.dfg.display_inst(inst, None)
539        ),
540    };
541
542    let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);
543
544    // Stack slots are required to be accessible and aligned.
545    let mflags = MemFlags::trusted();
546    pos.func.dfg.replace(inst).load(ty, mflags, addr, 0);
547}
548
549/// Expand illegal `stack_store` instructions.
550fn expand_stack_store(
551    inst: ir::Inst,
552    func: &mut ir::Function,
553    _cfg: &mut ControlFlowGraph,
554    isa: &dyn TargetIsa,
555) {
556    let addr_ty = isa.pointer_type();
557
558    let mut pos = FuncCursor::new(func).at_inst(inst);
559    pos.use_srcloc(inst);
560
561    let (val, stack_slot, offset) = match pos.func.dfg[inst] {
562        ir::InstructionData::StackStore {
563            opcode: _opcode,
564            arg,
565            stack_slot,
566            offset,
567        } => (arg, stack_slot, offset),
568        _ => panic!(
569            "Expected stack_store: {}",
570            pos.func.dfg.display_inst(inst, None)
571        ),
572    };
573
574    let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);
575
576    let mut mflags = MemFlags::new();
577    // Stack slots are required to be accessible and aligned.
578    mflags.set_notrap();
579    mflags.set_aligned();
580    pos.func.dfg.replace(inst).store(mflags, val, addr, 0);
581}
582
583/// Split a load into two parts before `iconcat`ing the result together.
584fn narrow_load(
585    inst: ir::Inst,
586    func: &mut ir::Function,
587    _cfg: &mut ControlFlowGraph,
588    _isa: &dyn TargetIsa,
589) {
590    let mut pos = FuncCursor::new(func).at_inst(inst);
591    pos.use_srcloc(inst);
592
593    let (ptr, offset, flags) = match pos.func.dfg[inst] {
594        ir::InstructionData::Load {
595            opcode: ir::Opcode::Load,
596            arg,
597            offset,
598            flags,
599        } => (arg, offset, flags),
600        _ => panic!("Expected load: {}", pos.func.dfg.display_inst(inst, None)),
601    };
602
603    let res_ty = pos.func.dfg.ctrl_typevar(inst);
604    let small_ty = res_ty.half_width().expect("Can't narrow load");
605
606    let al = pos.ins().load(small_ty, flags, ptr, offset);
607    let ah = pos.ins().load(
608        small_ty,
609        flags,
610        ptr,
611        offset.try_add_i64(8).expect("load offset overflow"),
612    );
613    pos.func.dfg.replace(inst).iconcat(al, ah);
614}
615
616/// Split a store into two parts after `isplit`ing the value.
617fn narrow_store(
618    inst: ir::Inst,
619    func: &mut ir::Function,
620    _cfg: &mut ControlFlowGraph,
621    _isa: &dyn TargetIsa,
622) {
623    let mut pos = FuncCursor::new(func).at_inst(inst);
624    pos.use_srcloc(inst);
625
626    let (val, ptr, offset, flags) = match pos.func.dfg[inst] {
627        ir::InstructionData::Store {
628            opcode: ir::Opcode::Store,
629            args,
630            offset,
631            flags,
632        } => (args[0], args[1], offset, flags),
633        _ => panic!("Expected store: {}", pos.func.dfg.display_inst(inst, None)),
634    };
635
636    let (al, ah) = pos.ins().isplit(val);
637    pos.ins().store(flags, al, ptr, offset);
638    pos.ins().store(
639        flags,
640        ah,
641        ptr,
642        offset.try_add_i64(8).expect("store offset overflow"),
643    );
644    pos.remove_inst();
645}
646
647/// Expands an illegal iconst value by splitting it into two.
648fn narrow_iconst(
649    inst: ir::Inst,
650    func: &mut ir::Function,
651    _cfg: &mut ControlFlowGraph,
652    isa: &dyn TargetIsa,
653) {
654    let imm: i64 = if let ir::InstructionData::UnaryImm {
655        opcode: ir::Opcode::Iconst,
656        imm,
657    } = &func.dfg[inst]
658    {
659        (*imm).into()
660    } else {
661        panic!("unexpected instruction in narrow_iconst");
662    };
663
664    let mut pos = FuncCursor::new(func).at_inst(inst);
665    pos.use_srcloc(inst);
666
667    let ty = pos.func.dfg.ctrl_typevar(inst);
668    if isa.pointer_bits() == 32 && ty == I64 {
669        let low = pos.ins().iconst(I32, imm & 0xffffffff);
670        let high = pos.ins().iconst(I32, imm >> 32);
671        // The instruction has as many results as iconcat, so no need to replace them.
672        pos.func.dfg.replace(inst).iconcat(low, high);
673        return;
674    }
675
676    unimplemented!("missing encoding or legalization for iconst.{:?}", ty);
677}
678
679fn narrow_icmp_imm(
680    inst: ir::Inst,
681    func: &mut ir::Function,
682    _cfg: &mut ControlFlowGraph,
683    _isa: &dyn TargetIsa,
684) {
685    use crate::ir::condcodes::{CondCode, IntCC};
686
687    let (arg, cond, imm): (ir::Value, IntCC, i64) = match func.dfg[inst] {
688        ir::InstructionData::IntCompareImm {
689            opcode: ir::Opcode::IcmpImm,
690            arg,
691            cond,
692            imm,
693        } => (arg, cond, imm.into()),
694        _ => panic!("unexpected instruction in narrow_icmp_imm"),
695    };
696
697    let mut pos = FuncCursor::new(func).at_inst(inst);
698    pos.use_srcloc(inst);
699
700    let ty = pos.func.dfg.ctrl_typevar(inst);
701    let ty_half = ty.half_width().unwrap();
702
703    let imm_low = pos
704        .ins()
705        .iconst(ty_half, imm & (1u128 << (ty_half.bits() - 1)) as i64);
706    let imm_high = pos
707        .ins()
708        .iconst(ty_half, imm.wrapping_shr(ty_half.bits().into()));
709    let (arg_low, arg_high) = pos.ins().isplit(arg);
710
711    match cond {
712        IntCC::Equal => {
713            let res_low = pos.ins().icmp(cond, arg_low, imm_low);
714            let res_high = pos.ins().icmp(cond, arg_high, imm_high);
715            pos.func.dfg.replace(inst).band(res_low, res_high);
716        }
717        IntCC::NotEqual => {
718            let res_low = pos.ins().icmp(cond, arg_low, imm_low);
719            let res_high = pos.ins().icmp(cond, arg_high, imm_high);
720            pos.func.dfg.replace(inst).bor(res_low, res_high);
721        }
722        IntCC::SignedGreaterThan
723        | IntCC::SignedGreaterThanOrEqual
724        | IntCC::SignedLessThan
725        | IntCC::SignedLessThanOrEqual
726        | IntCC::UnsignedGreaterThan
727        | IntCC::UnsignedGreaterThanOrEqual
728        | IntCC::UnsignedLessThan
729        | IntCC::UnsignedLessThanOrEqual => {
730            let b1 = pos.ins().icmp(cond.without_equal(), arg_high, imm_high);
731            let b2 = pos
732                .ins()
733                .icmp(cond.inverse().without_equal(), arg_high, imm_high);
734            let b3 = pos.ins().icmp(cond.unsigned(), arg_low, imm_low);
735            let c1 = pos.ins().bnot(b2);
736            let c2 = pos.ins().band(c1, b3);
737            pos.func.dfg.replace(inst).bor(b1, c2);
738        }
739        _ => unimplemented!("missing legalization for condition {:?}", cond),
740    }
741}