//! 32-bit ARM ISA: binary code emission. use crate::binemit::{Reloc, StackMap}; use crate::ir::SourceLoc; use crate::isa::arm32::inst::*; use core::convert::TryFrom; use log::debug; /// Memory addressing mode finalization: convert "special" modes (e.g., /// nominal stack offset) into real addressing modes, possibly by /// emitting some helper instructions that come immediately before the use /// of this amode. pub fn mem_finalize(mem: &AMode, state: &EmitState) -> (SmallVec<[Inst; 4]>, AMode) { match mem { &AMode::RegOffset(_, off) | &AMode::SPOffset(off, _) | &AMode::FPOffset(off, _) | &AMode::NominalSPOffset(off, _) => { let basereg = match mem { &AMode::RegOffset(reg, _) => reg, &AMode::SPOffset(..) | &AMode::NominalSPOffset(..) => sp_reg(), &AMode::FPOffset(..) => fp_reg(), _ => unreachable!(), }; let adj = match mem { &AMode::NominalSPOffset(..) => { debug!( "mem_finalize: nominal SP offset {} + adj {} -> {}", off, state.virtual_sp_offset, off + state.virtual_sp_offset ); state.virtual_sp_offset } _ => 0, }; let off = off + adj; assert!(-(1 << 31) <= off && off <= (1 << 32)); if let Some(off) = UImm12::maybe_from_i64(off) { let mem = AMode::RegOffset12(basereg, off); (smallvec![], mem) } else { let tmp = writable_ip_reg(); let const_insts = Inst::load_constant(tmp, off as u32); let mem = AMode::reg_plus_reg(basereg, tmp.to_reg(), 0); (const_insts, mem) } } // Just assert immediate is valid here. _ => (smallvec![], mem.clone()), } } //============================================================================= // Instructions and subcomponents: emission fn machreg_to_gpr(m: Reg) -> u16 { assert_eq!(m.get_class(), RegClass::I32); u16::try_from(m.to_real_reg().get_hw_encoding()).unwrap() } fn machreg_to_gpr_lo(m: Reg) -> u16 { let gpr_lo = machreg_to_gpr(m); assert!(gpr_lo < 8); gpr_lo } fn machreg_is_lo(m: Reg) -> bool { machreg_to_gpr(m) < 8 } fn enc_16_rr(bits_15_6: u16, rd: Reg, rm: Reg) -> u16 { (bits_15_6 << 6) | machreg_to_gpr_lo(rd) | (machreg_to_gpr_lo(rm) << 3) } fn enc_16_rr_any(bits_15_8: u16, rd: Reg, rm: Reg) -> u16 { let rd = machreg_to_gpr(rd); (bits_15_8 << 8) | (rd & 0x7) | ((rd >> 3) << 7) | (machreg_to_gpr(rm) << 3) } fn enc_16_mov(rd: Writable, rm: Reg) -> u16 { enc_16_rr_any(0b01000110, rd.to_reg(), rm) } fn enc_16_it(cond: Cond, insts: &Vec) -> u16 { let cond = cond.bits(); let mut mask: u16 = 0; for inst in insts.iter().skip(1) { if inst.then { mask |= cond & 0x1; } else { mask |= (cond & 0x1) ^ 0x1; } mask <<= 1; } mask |= 0x1; mask <<= 4 - insts.len(); 0b1011_1111_0000_0000 | (cond << 4) | mask } fn enc_32_regs( mut inst: u32, reg_0: Option, reg_8: Option, reg_12: Option, reg_16: Option, ) -> u32 { if let Some(reg_0) = reg_0 { inst |= u32::from(machreg_to_gpr(reg_0)); } if let Some(reg_8) = reg_8 { inst |= u32::from(machreg_to_gpr(reg_8)) << 8; } if let Some(reg_12) = reg_12 { inst |= u32::from(machreg_to_gpr(reg_12)) << 12; } if let Some(reg_16) = reg_16 { inst |= u32::from(machreg_to_gpr(reg_16)) << 16; } inst } fn enc_32_reg_shift(inst: u32, shift: &Option) -> u32 { match shift { Some(shift) => { let op = u32::from(shift.op().bits()); let amt = u32::from(shift.amt().value()); let imm2 = amt & 0x3; let imm3 = (amt >> 2) & 0x7; inst | (op << 4) | (imm2 << 6) | (imm3 << 12) } None => inst, } } fn enc_32_r_imm16(bits_31_20: u32, rd: Reg, imm16: u16) -> u32 { let imm16 = u32::from(imm16); let imm8 = imm16 & 0xff; let imm3 = (imm16 >> 8) & 0x7; let i = (imm16 >> 11) & 0x1; let imm4 = (imm16 >> 12) & 0xf; let inst = ((bits_31_20 << 20) & !(1 << 26)) | imm8 | (imm3 << 12) | (imm4 << 16) | (i << 26); enc_32_regs(inst, None, Some(rd), None, None) } fn enc_32_rrr(bits_31_20: u32, bits_15_12: u32, bits_7_4: u32, rd: Reg, rm: Reg, rn: Reg) -> u32 { let inst = (bits_31_20 << 20) | (bits_15_12 << 12) | (bits_7_4 << 4); enc_32_regs(inst, Some(rm), Some(rd), None, Some(rn)) } fn enc_32_imm12(inst: u32, imm12: UImm12) -> u32 { let imm12 = imm12.bits(); let imm8 = imm12 & 0xff; let imm3 = (imm12 >> 8) & 0x7; let i = (imm12 >> 11) & 0x1; inst | imm8 | (imm3 << 12) | (i << 26) } fn enc_32_mem_r(bits_24_20: u32, rt: Reg, rn: Reg, rm: Reg, imm2: u8) -> u32 { let imm2 = u32::from(imm2); let inst = (imm2 << 4) | (bits_24_20 << 20) | (0b11111 << 27); enc_32_regs(inst, Some(rm), None, Some(rt), Some(rn)) } fn enc_32_mem_off12(bits_24_20: u32, rt: Reg, rn: Reg, off12: UImm12) -> u32 { let off12 = off12.bits(); let inst = off12 | (bits_24_20 << 20) | (0b11111 << 27); enc_32_regs(inst, None, None, Some(rt), Some(rn)) } fn enc_32_jump(target: BranchTarget) -> u32 { let off24 = target.as_off24(); let imm11 = off24 & 0x7ff; let imm10 = (off24 >> 11) & 0x3ff; let i2 = (off24 >> 21) & 0x1; let i1 = (off24 >> 22) & 0x1; let s = (off24 >> 23) & 0x1; let j1 = (i1 ^ s) ^ 1; let j2 = (i2 ^ s) ^ 1; 0b11110_0_0000000000_10_0_1_0_00000000000 | imm11 | (j2 << 11) | (j1 << 13) | (imm10 << 16) | (s << 26) } fn enc_32_cond_branch(cond: Cond, target: BranchTarget) -> u32 { let cond = u32::from(cond.bits()); let off20 = target.as_off20(); let imm11 = off20 & 0x7ff; let imm6 = (off20 >> 11) & 0x3f; let j1 = (off20 >> 17) & 0x1; let j2 = (off20 >> 18) & 0x1; let s = (off20 >> 19) & 0x1; 0b11110_0_0000_000000_10_0_0_0_00000000000 | imm11 | (j2 << 11) | (j1 << 13) | (imm6 << 16) | (cond << 22) | (s << 26) } fn u32_swap_halfwords(x: u32) -> u32 { (x >> 16) | (x << 16) } fn emit_32(inst: u32, sink: &mut MachBuffer) { let inst_hi = (inst >> 16) as u16; let inst_lo = (inst & 0xffff) as u16; sink.put2(inst_hi); sink.put2(inst_lo); } /// State carried between emissions of a sequence of instructions. #[derive(Default, Clone, Debug)] pub struct EmitState { /// Addend to convert nominal-SP offsets to real-SP offsets at the current /// program point. pub(crate) virtual_sp_offset: i64, /// Offset of FP from nominal-SP. pub(crate) nominal_sp_to_fp: i64, /// Safepoint stack map for upcoming instruction, as provided to `pre_safepoint()`. stack_map: Option, /// Source location of next machine code instruction to be emitted. cur_srcloc: SourceLoc, } impl MachInstEmitState for EmitState { fn new(abi: &dyn ABICallee) -> Self { EmitState { virtual_sp_offset: 0, nominal_sp_to_fp: abi.frame_size() as i64, stack_map: None, cur_srcloc: SourceLoc::default(), } } fn pre_safepoint(&mut self, stack_map: StackMap) { self.stack_map = Some(stack_map); } fn pre_sourceloc(&mut self, srcloc: SourceLoc) { self.cur_srcloc = srcloc; } } impl EmitState { fn take_stack_map(&mut self) -> Option { self.stack_map.take() } fn clear_post_insn(&mut self) { self.stack_map = None; } fn cur_srcloc(&self) -> SourceLoc { self.cur_srcloc } } pub struct EmitInfo { flags: settings::Flags, } impl EmitInfo { pub(crate) fn new(flags: settings::Flags) -> Self { EmitInfo { flags } } } impl MachInstEmitInfo for EmitInfo { fn flags(&self) -> &settings::Flags { &self.flags } } impl MachInstEmit for Inst { type Info = EmitInfo; type State = EmitState; fn emit(&self, sink: &mut MachBuffer, emit_info: &Self::Info, state: &mut EmitState) { let start_off = sink.cur_offset(); match self { &Inst::Nop0 | &Inst::EpiloguePlaceholder => {} &Inst::Nop2 => { sink.put2(0b1011_1111_0000_0000); } &Inst::AluRRR { alu_op, rd, rn, rm } => { let (bits_31_20, bits_15_12, bits_7_4) = match alu_op { ALUOp::Lsl => (0b111110100000, 0b1111, 0b0000), ALUOp::Lsr => (0b111110100010, 0b1111, 0b0000), ALUOp::Asr => (0b111110100100, 0b1111, 0b0000), ALUOp::Ror => (0b111110100110, 0b1111, 0b0000), ALUOp::Qadd => (0b111110101000, 0b1111, 0b1000), ALUOp::Qsub => (0b111110101000, 0b1111, 0b1010), ALUOp::Mul => (0b111110110000, 0b1111, 0b0000), ALUOp::Udiv => (0b111110111011, 0b1111, 0b1111), ALUOp::Sdiv => (0b111110111001, 0b1111, 0b1111), _ => panic!("Invalid ALUOp {:?} in RRR form!", alu_op), }; emit_32( enc_32_rrr(bits_31_20, bits_15_12, bits_7_4, rd.to_reg(), rm, rn), sink, ); } &Inst::AluRRRShift { alu_op, rd, rn, rm, ref shift, } => { let bits_31_24 = 0b111_0101; let bits_24_20 = match alu_op { ALUOp::And => 0b00000, ALUOp::Bic => 0b00010, ALUOp::Orr => 0b00100, ALUOp::Orn => 0b00110, ALUOp::Eor => 0b01000, ALUOp::Add => 0b10000, ALUOp::Adds => 0b10001, ALUOp::Adc => 0b10100, ALUOp::Adcs => 0b10101, ALUOp::Sbc => 0b10110, ALUOp::Sbcs => 0b10111, ALUOp::Sub => 0b11010, ALUOp::Subs => 0b11011, ALUOp::Rsb => 0b11100, _ => panic!("Invalid ALUOp {:?} in RRRShift form!", alu_op), }; let bits_31_20 = (bits_31_24 << 5) | bits_24_20; let inst = enc_32_rrr(bits_31_20, 0, 0, rd.to_reg(), rm, rn); let inst = enc_32_reg_shift(inst, shift); emit_32(inst, sink); } &Inst::AluRRShift { alu_op, rd, rm, ref shift, } => { let bits_24_21 = match alu_op { ALUOp1::Mvn => 0b0011, ALUOp1::Mov => 0b0010, }; let inst = 0b1110101_0000_0_1111_0_000_0000_00_00_0000 | (bits_24_21 << 21); let inst = enc_32_regs(inst, Some(rm), Some(rd.to_reg()), None, None); let inst = enc_32_reg_shift(inst, shift); emit_32(inst, sink); } &Inst::AluRRRR { alu_op, rd_hi, rd_lo, rn, rm, } => { let (bits_22_20, bits_7_4) = match alu_op { ALUOp::Smull => (0b000, 0b0000), ALUOp::Umull => (0b010, 0b0000), _ => panic!("Invalid ALUOp {:?} in RRRR form!", alu_op), }; let inst = (0b111110111 << 23) | (bits_22_20 << 20) | (bits_7_4 << 4); let inst = enc_32_regs( inst, Some(rm), Some(rd_hi.to_reg()), Some(rd_lo.to_reg()), Some(rn), ); emit_32(inst, sink); } &Inst::AluRRImm12 { alu_op, rd, rn, imm12, } => { let bits_24_20 = match alu_op { ALUOp::Add => 0b00000, ALUOp::Sub => 0b01010, _ => panic!("Invalid ALUOp {:?} in RRImm12 form!", alu_op), }; let inst = (0b11110_0_1 << 25) | (bits_24_20 << 20); let inst = enc_32_regs(inst, None, Some(rd.to_reg()), None, Some(rn)); let inst = enc_32_imm12(inst, imm12); emit_32(inst, sink); } &Inst::AluRRImm8 { alu_op, rd, rn, imm8, } => { let bits_24_20 = match alu_op { ALUOp::And => 0b00000, ALUOp::Bic => 0b00010, ALUOp::Orr => 0b00100, ALUOp::Orn => 0b00110, ALUOp::Eor => 0b01000, ALUOp::Add => 0b10000, ALUOp::Adds => 0b10001, ALUOp::Adc => 0b10100, ALUOp::Adcs => 0b10101, ALUOp::Sbc => 0b10110, ALUOp::Sbcs => 0b10111, ALUOp::Sub => 0b11010, ALUOp::Subs => 0b11011, ALUOp::Rsb => 0b11100, _ => panic!("Invalid ALUOp {:?} in RRImm8 form!", alu_op), }; let imm8 = imm8.bits(); let inst = 0b11110_0_0_00000_0000_0_000_0000_00000000 | imm8 | (bits_24_20 << 20); let inst = enc_32_regs(inst, None, Some(rd.to_reg()), None, Some(rn)); emit_32(inst, sink); } &Inst::AluRImm8 { alu_op, rd, imm8 } => { let bits_24_20 = match alu_op { ALUOp1::Mvn => 0b00110, ALUOp1::Mov => 0b00100, }; let imm8 = imm8.bits(); let inst = 0b11110_0_0_00000_1111_0_000_0000_00000000 | imm8 | (bits_24_20 << 20); let inst = enc_32_regs(inst, None, Some(rd.to_reg()), None, None); emit_32(inst, sink); } &Inst::BitOpRR { bit_op, rd, rm } => { let (bits_22_20, bits_7_4) = match bit_op { BitOp::Rbit => (0b001, 0b1010), BitOp::Rev => (0b001, 0b1000), BitOp::Clz => (0b011, 0b1000), }; let inst = 0b111110101_000_0000_1111_0000_0000_0000 | (bits_22_20 << 20) | (bits_7_4 << 4); let inst = enc_32_regs(inst, Some(rm), Some(rd.to_reg()), None, Some(rm)); emit_32(inst, sink); } &Inst::Mov { rd, rm } => { sink.put2(enc_16_mov(rd, rm)); } &Inst::MovImm16 { rd, imm16 } => { emit_32(enc_32_r_imm16(0b11110_0_100100, rd.to_reg(), imm16), sink); } &Inst::Movt { rd, imm16 } => { emit_32(enc_32_r_imm16(0b11110_0_101100, rd.to_reg(), imm16), sink); } &Inst::Cmp { rn, rm } => { // Check which 16-bit encoding is allowed. if machreg_is_lo(rn) && machreg_is_lo(rm) { sink.put2(enc_16_rr(0b0100001010, rn, rm)); } else { sink.put2(enc_16_rr_any(0b01000101, rn, rm)); } } &Inst::CmpImm8 { rn, imm8 } => { let inst = 0b11110_0_011011_0000_0_000_1111_00000000 | u32::from(imm8); let inst = enc_32_regs(inst, None, None, None, Some(rn)); emit_32(inst, sink); } &Inst::Store { rt, ref mem, bits } => { let (mem_insts, mem) = mem_finalize(mem, state); for inst in mem_insts.into_iter() { inst.emit(sink, emit_info, state); } let srcloc = state.cur_srcloc(); if srcloc != SourceLoc::default() { // Register the offset at which the store instruction starts. sink.add_trap(srcloc, TrapCode::HeapOutOfBounds); } match mem { AMode::RegReg(rn, rm, imm2) => { let bits_24_20 = match bits { 32 => 0b00100, 16 => 0b00010, 8 => 0b00000, _ => panic!("Unsupported store case {:?}", self), }; emit_32(enc_32_mem_r(bits_24_20, rt, rn, rm, imm2), sink); } AMode::RegOffset12(rn, off12) => { let bits_24_20 = match bits { 32 => 0b01100, 16 => 0b01010, 8 => 0b01000, _ => panic!("Unsupported store case {:?}", self), }; emit_32(enc_32_mem_off12(bits_24_20, rt, rn, off12), sink); } AMode::PCRel(_) => panic!("Unsupported store case {:?}", self), _ => unreachable!(), } } &Inst::Load { rt, ref mem, bits, sign_extend, } => { let (mem_insts, mem) = mem_finalize(mem, state); for inst in mem_insts.into_iter() { inst.emit(sink, emit_info, state); } let srcloc = state.cur_srcloc(); if srcloc != SourceLoc::default() { // Register the offset at which the load instruction starts. sink.add_trap(srcloc, TrapCode::HeapOutOfBounds); } match mem { AMode::RegReg(rn, rm, imm2) => { let bits_24_20 = match (bits, sign_extend) { (32, _) => 0b00101, (16, true) => 0b10011, (16, false) => 0b00011, (8, true) => 0b10001, (8, false) => 0b00001, _ => panic!("Unsupported load case {:?}", self), }; emit_32(enc_32_mem_r(bits_24_20, rt.to_reg(), rn, rm, imm2), sink); } AMode::RegOffset12(rn, off12) => { let bits_24_20 = match (bits, sign_extend) { (32, _) => 0b01101, (16, true) => 0b11011, (16, false) => 0b01011, (8, true) => 0b11001, (8, false) => 0b01001, _ => panic!("Unsupported load case {:?}", self), }; emit_32(enc_32_mem_off12(bits_24_20, rt.to_reg(), rn, off12), sink); } AMode::PCRel(off12) => { let mut bits_24_20 = match (bits, sign_extend) { (32, _) => 0b00101, (16, true) => 0b10011, (16, false) => 0b00011, (8, true) => 0b10001, (8, false) => 0b00001, _ => panic!("Unsupported load case {:?}", self), }; let (u, off12) = if off12 > 0 { (1, off12) } else { (0, -off12) }; let off12 = UImm12::maybe_from_i64(i64::from(off12)).unwrap(); bits_24_20 |= u << 3; emit_32( enc_32_mem_off12(bits_24_20, rt.to_reg(), pc_reg(), off12), sink, ); } _ => unreachable!(), } } &Inst::LoadAddr { rd, ref mem } => { let (mem_insts, mem) = mem_finalize(mem, state); for inst in mem_insts.into_iter() { inst.emit(sink, emit_info, state); } let inst = match mem { AMode::RegReg(reg1, reg2, shift) => { let shift = u32::from(shift); let shift_amt = ShiftOpShiftImm::maybe_from_shift(shift).unwrap(); let shift = ShiftOpAndAmt::new(ShiftOp::LSL, shift_amt); Inst::AluRRRShift { alu_op: ALUOp::Add, rd, rn: reg1, rm: reg2, shift: Some(shift), } } AMode::RegOffset12(reg, imm12) => Inst::AluRRImm12 { alu_op: ALUOp::Add, rd, rn: reg, imm12, }, AMode::PCRel(off12) => { let (off12, alu_op) = if off12 > 0 { (off12, ALUOp::Add) } else { (-off12, ALUOp::Sub) }; let imm12 = UImm12::maybe_from_i64(i64::from(off12)).unwrap(); Inst::AluRRImm12 { alu_op, rd, rn: pc_reg(), imm12, } } _ => unreachable!(), }; inst.emit(sink, emit_info, state); } &Inst::Extend { rd, rm, from_bits, signed, } if from_bits >= 8 => { let rd = rd.to_reg(); if machreg_is_lo(rd) && machreg_is_lo(rm) { let bits_15_9 = match (from_bits, signed) { (16, true) => 0b1011001000, (16, false) => 0b1011001010, (8, true) => 0b1011001001, (8, false) => 0b1011001011, _ => panic!("Unsupported Extend case: {:?}", self), }; sink.put2(enc_16_rr(bits_15_9, rd, rm)); } else { let bits_22_20 = match (from_bits, signed) { (16, true) => 0b000, (16, false) => 0b001, (8, true) => 0b100, (8, false) => 0b101, _ => panic!("Unsupported Extend case: {:?}", self), }; let inst = 0b111110100_000_11111111_0000_1000_0000 | (bits_22_20 << 20); let inst = enc_32_regs(inst, Some(rm), Some(rd), None, None); emit_32(inst, sink); } } &Inst::Extend { rd, rm, from_bits, signed, } if from_bits == 1 => { let inst = Inst::AluRRImm8 { alu_op: ALUOp::And, rd, rn: rm, imm8: UImm8::maybe_from_i64(1).unwrap(), }; inst.emit(sink, emit_info, state); if signed { let inst = Inst::AluRRImm8 { alu_op: ALUOp::Rsb, rd, rn: rd.to_reg(), imm8: UImm8::maybe_from_i64(1).unwrap(), }; inst.emit(sink, emit_info, state); } } &Inst::Extend { .. } => { panic!("Unsupported extend variant"); } &Inst::It { cond, ref insts } => { assert!(1 <= insts.len() && insts.len() <= 4); assert!(insts[0].then); sink.put2(enc_16_it(cond, insts)); for inst in insts.iter() { inst.inst.emit(sink, emit_info, state); } } &Inst::Push { ref reg_list } => match reg_list.len() { 0 => panic!("Unsupported Push case: {:?}", self), 1 => { let reg = u32::from(machreg_to_gpr(reg_list[0])); let inst: u32 = 0b1111100001001101_0000_110100000100 | (reg << 12); emit_32(inst, sink); } _ => { let mut inst: u32 = 0b1110100100101101 << 16; for reg in reg_list { inst |= 1 << machreg_to_gpr(*reg); } if inst & ((1 << 13) | (1 << 15)) != 0 { panic!("Unsupported Push case: {:?}", self); } emit_32(inst, sink); } }, &Inst::Pop { ref reg_list } => match reg_list.len() { 0 => panic!("Unsupported Pop case: {:?}", self), 1 => { let reg = u32::from(machreg_to_gpr(reg_list[0].to_reg())); let inst: u32 = 0b1111100001011101_0000_101100000100 | (reg << 12); emit_32(inst, sink); } _ => { let mut inst: u32 = 0b1110100010111101 << 16; for reg in reg_list { inst |= 1 << machreg_to_gpr(reg.to_reg()); } if (inst & (1 << 14) != 0) && (inst & (1 << 15) != 0) { panic!("Unsupported Pop case: {:?}", self); } emit_32(inst, sink); } }, &Inst::Call { ref info } => { let srcloc = state.cur_srcloc(); sink.add_reloc(srcloc, Reloc::Arm32Call, &info.dest, 0); emit_32(0b11110_0_0000000000_11_0_1_0_00000000000, sink); if info.opcode.is_call() { sink.add_call_site(srcloc, info.opcode); } } &Inst::CallInd { ref info } => { let srcloc = state.cur_srcloc(); sink.put2(0b01000111_1_0000_000 | (machreg_to_gpr(info.rm) << 3)); if info.opcode.is_call() { sink.add_call_site(srcloc, info.opcode); } } &Inst::LoadExtName { rt, ref name, offset, } => { // maybe nop2 (0|2) bytes (pc is now 4-aligned) // ldr rt, [pc, #4] 4 bytes // b continue 4 bytes // addr 4 bytes // continue: // if start_off & 0x3 != 0 { Inst::Nop2.emit(sink, emit_info, state); } assert_eq!(sink.cur_offset() & 0x3, 0); let mem = AMode::PCRel(4); let inst = Inst::Load { rt, mem, bits: 32, sign_extend: false, }; inst.emit(sink, emit_info, state); let inst = Inst::Jump { dest: BranchTarget::ResolvedOffset(4), }; inst.emit(sink, emit_info, state); let srcloc = state.cur_srcloc(); sink.add_reloc(srcloc, Reloc::Abs4, name, offset.into()); sink.put4(0); } &Inst::Ret => { sink.put2(0b010001110_1110_000); // bx lr } &Inst::Jump { dest } => { let off = sink.cur_offset(); // Indicate that the jump uses a label, if so, so that a fixup can occur later. if let Some(l) = dest.as_label() { sink.use_label_at_offset(off, l, LabelUse::Branch24); sink.add_uncond_branch(off, off + 4, l); } emit_32(enc_32_jump(dest), sink); } &Inst::CondBr { taken, not_taken, cond, } => { // Conditional part first. let cond_off = sink.cur_offset(); if let Some(l) = taken.as_label() { let label_use = LabelUse::Branch20; sink.use_label_at_offset(cond_off, l, label_use); let inverted = enc_32_cond_branch(cond.invert(), taken); let inverted = u32_swap_halfwords(inverted).to_le_bytes(); sink.add_cond_branch(cond_off, cond_off + 4, l, &inverted[..]); } emit_32(enc_32_cond_branch(cond, taken), sink); // Unconditional part. let uncond_off = sink.cur_offset(); if let Some(l) = not_taken.as_label() { sink.use_label_at_offset(uncond_off, l, LabelUse::Branch24); sink.add_uncond_branch(uncond_off, uncond_off + 4, l); } emit_32(enc_32_jump(not_taken), sink); } &Inst::IndirectBr { rm, .. } => { let inst = 0b010001110_0000_000 | (machreg_to_gpr(rm) << 3); sink.put2(inst); } &Inst::Udf { trap_info } => { let srcloc = state.cur_srcloc(); let code = trap_info; sink.add_trap(srcloc, code); sink.put2(0b11011110_00000000); } &Inst::Bkpt => { sink.put2(0b10111110_00000000); } &Inst::TrapIf { cond, trap_info } => { let cond = cond.invert(); let dest = BranchTarget::ResolvedOffset(2); emit_32(enc_32_cond_branch(cond, dest), sink); let trap = Inst::Udf { trap_info }; trap.emit(sink, emit_info, state); } &Inst::VirtualSPOffsetAdj { offset } => { debug!( "virtual sp offset adjusted by {} -> {}", offset, state.virtual_sp_offset + offset, ); state.virtual_sp_offset += offset; } } let end_off = sink.cur_offset(); debug_assert!((end_off - start_off) <= Inst::worst_case_size()); } fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, state: &mut EmitState) -> String { self.print_with_state(mb_rru, state) } }