use crate::binemit::{Reloc, StackMap};
use crate::ir::condcodes::IntCC;
use crate::ir::MemFlags;
use crate::ir::{SourceLoc, TrapCode};
use crate::isa::s390x::inst::*;
use crate::isa::s390x::settings as s390x_settings;
use core::convert::TryFrom;
use regalloc::{Reg, RegClass};
pub fn mem_finalize(
mem: &MemArg,
state: &EmitState,
have_d12: bool,
have_d20: bool,
have_pcrel: bool,
have_index: bool,
) -> (SmallVec<[Inst; 4]>, MemArg) {
let mut insts = SmallVec::new();
let mem = match mem {
&MemArg::RegOffset { off, .. }
| &MemArg::InitialSPOffset { off }
| &MemArg::NominalSPOffset { off } => {
let base = match mem {
&MemArg::RegOffset { reg, .. } => reg,
&MemArg::InitialSPOffset { .. } | &MemArg::NominalSPOffset { .. } => stack_reg(),
_ => unreachable!(),
};
let adj = match mem {
&MemArg::InitialSPOffset { .. } => {
state.initial_sp_offset + state.virtual_sp_offset
}
&MemArg::NominalSPOffset { .. } => state.virtual_sp_offset,
_ => 0,
};
let off = off + adj;
if let Some(disp) = UImm12::maybe_from_u64(off as u64) {
MemArg::BXD12 {
base,
index: zero_reg(),
disp,
flags: mem.get_flags(),
}
} else if let Some(disp) = SImm20::maybe_from_i64(off) {
MemArg::BXD20 {
base,
index: zero_reg(),
disp,
flags: mem.get_flags(),
}
} else {
let tmp = writable_spilltmp_reg();
assert!(base != tmp.to_reg());
insts.extend(Inst::load_constant64(tmp, off as u64));
MemArg::reg_plus_reg(base, tmp.to_reg(), mem.get_flags())
}
}
_ => mem.clone(),
};
let need_load_address = match &mem {
&MemArg::Label { .. } | &MemArg::Symbol { .. } if !have_pcrel => true,
&MemArg::BXD20 { .. } if !have_d20 => true,
&MemArg::BXD12 { index, .. } | &MemArg::BXD20 { index, .. } if !have_index => {
index != zero_reg()
}
_ => false,
};
let mem = if need_load_address {
let flags = mem.get_flags();
let tmp = writable_spilltmp_reg();
insts.push(Inst::LoadAddr { rd: tmp, mem });
MemArg::reg(tmp.to_reg(), flags)
} else {
mem
};
let mem = match &mem {
&MemArg::BXD12 {
base,
index,
disp,
flags,
} if !have_d12 => {
assert!(have_d20);
MemArg::BXD20 {
base,
index,
disp: SImm20::from_uimm12(disp),
flags,
}
}
_ => mem,
};
(insts, mem)
}
pub fn mem_emit(
rd: Reg,
mem: &MemArg,
opcode_rx: Option<u16>,
opcode_rxy: Option<u16>,
opcode_ril: Option<u16>,
add_trap: bool,
sink: &mut MachBuffer<Inst>,
emit_info: &EmitInfo,
state: &mut EmitState,
) {
let (mem_insts, mem) = mem_finalize(
mem,
state,
opcode_rx.is_some(),
opcode_rxy.is_some(),
opcode_ril.is_some(),
true,
);
for inst in mem_insts.into_iter() {
inst.emit(sink, emit_info, state);
}
if add_trap && mem.can_trap() {
let srcloc = state.cur_srcloc();
if srcloc != SourceLoc::default() {
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
}
}
match &mem {
&MemArg::BXD12 {
base, index, disp, ..
} => {
put(
sink,
&enc_rx(opcode_rx.unwrap(), rd, base, index, disp.bits()),
);
}
&MemArg::BXD20 {
base, index, disp, ..
} => {
put(
sink,
&enc_rxy(opcode_rxy.unwrap(), rd, base, index, disp.bits()),
);
}
&MemArg::Label { ref target } => {
if let Some(l) = target.as_label() {
sink.use_label_at_offset(sink.cur_offset(), l, LabelUse::BranchRIL);
}
put(
sink,
&enc_ril_b(opcode_ril.unwrap(), rd, target.as_ril_offset_or_zero()),
);
}
&MemArg::Symbol {
ref name, offset, ..
} => {
let reloc = Reloc::S390xPCRel32Dbl;
let srcloc = state.cur_srcloc();
put_with_reloc(
sink,
&enc_ril_b(opcode_ril.unwrap(), rd, 0),
2,
srcloc,
reloc,
name,
offset.into(),
);
}
_ => unreachable!(),
}
}
pub fn mem_rs_emit(
rd: Reg,
rn: Reg,
mem: &MemArg,
opcode_rs: Option<u16>,
opcode_rsy: Option<u16>,
add_trap: bool,
sink: &mut MachBuffer<Inst>,
emit_info: &EmitInfo,
state: &mut EmitState,
) {
let (mem_insts, mem) = mem_finalize(
mem,
state,
opcode_rs.is_some(),
opcode_rsy.is_some(),
false,
false,
);
for inst in mem_insts.into_iter() {
inst.emit(sink, emit_info, state);
}
if add_trap && mem.can_trap() {
let srcloc = state.cur_srcloc();
if srcloc != SourceLoc::default() {
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
}
}
match &mem {
&MemArg::BXD12 {
base, index, disp, ..
} => {
assert!(index == zero_reg());
put(sink, &enc_rs(opcode_rs.unwrap(), rd, rn, base, disp.bits()));
}
&MemArg::BXD20 {
base, index, disp, ..
} => {
assert!(index == zero_reg());
put(
sink,
&enc_rsy(opcode_rsy.unwrap(), rd, rn, base, disp.bits()),
);
}
_ => unreachable!(),
}
}
pub fn mem_imm8_emit(
imm: u8,
mem: &MemArg,
opcode_si: u16,
opcode_siy: u16,
add_trap: bool,
sink: &mut MachBuffer<Inst>,
emit_info: &EmitInfo,
state: &mut EmitState,
) {
let (mem_insts, mem) = mem_finalize(mem, state, true, true, false, false);
for inst in mem_insts.into_iter() {
inst.emit(sink, emit_info, state);
}
if add_trap && mem.can_trap() {
let srcloc = state.cur_srcloc();
if srcloc != SourceLoc::default() {
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
}
}
match &mem {
&MemArg::BXD12 {
base, index, disp, ..
} => {
assert!(index == zero_reg());
put(sink, &enc_si(opcode_si, base, disp.bits(), imm));
}
&MemArg::BXD20 {
base, index, disp, ..
} => {
assert!(index == zero_reg());
put(sink, &enc_siy(opcode_siy, base, disp.bits(), imm));
}
_ => unreachable!(),
}
}
pub fn mem_imm16_emit(
imm: i16,
mem: &MemArg,
opcode_sil: u16,
add_trap: bool,
sink: &mut MachBuffer<Inst>,
emit_info: &EmitInfo,
state: &mut EmitState,
) {
let (mem_insts, mem) = mem_finalize(mem, state, true, false, false, false);
for inst in mem_insts.into_iter() {
inst.emit(sink, emit_info, state);
}
if add_trap && mem.can_trap() {
let srcloc = state.cur_srcloc();
if srcloc != SourceLoc::default() {
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
}
}
match &mem {
&MemArg::BXD12 {
base, index, disp, ..
} => {
assert!(index == zero_reg());
put(sink, &enc_sil(opcode_sil, base, disp.bits(), imm));
}
_ => unreachable!(),
}
}
fn machreg_to_gpr(m: Reg) -> u8 {
assert_eq!(m.get_class(), RegClass::I64);
u8::try_from(m.to_real_reg().get_hw_encoding()).unwrap()
}
fn machreg_to_fpr(m: Reg) -> u8 {
assert_eq!(m.get_class(), RegClass::F64);
u8::try_from(m.to_real_reg().get_hw_encoding()).unwrap()
}
fn machreg_to_gpr_or_fpr(m: Reg) -> u8 {
u8::try_from(m.to_real_reg().get_hw_encoding()).unwrap()
}
fn enc_e(opcode: u16) -> [u8; 2] {
let mut enc: [u8; 2] = [0; 2];
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
enc[0] = opcode1;
enc[1] = opcode2;
enc
}
fn enc_ri_a(opcode: u16, r1: Reg, i2: u16) -> [u8; 4] {
let mut enc: [u8; 4] = [0; 4];
let opcode1 = ((opcode >> 4) & 0xff) as u8;
let opcode2 = (opcode & 0xf) as u8;
let r1 = machreg_to_gpr(r1) & 0x0f;
enc[0] = opcode1;
enc[1] = r1 << 4 | opcode2;
enc[2..].copy_from_slice(&i2.to_be_bytes());
enc
}
fn enc_ri_b(opcode: u16, r1: Reg, ri2: i32) -> [u8; 4] {
let mut enc: [u8; 4] = [0; 4];
let opcode1 = ((opcode >> 4) & 0xff) as u8;
let opcode2 = (opcode & 0xf) as u8;
let r1 = machreg_to_gpr(r1) & 0x0f;
let ri2 = ((ri2 >> 1) & 0xffff) as u16;
enc[0] = opcode1;
enc[1] = r1 << 4 | opcode2;
enc[2..].copy_from_slice(&ri2.to_be_bytes());
enc
}
fn enc_ri_c(opcode: u16, m1: u8, ri2: i32) -> [u8; 4] {
let mut enc: [u8; 4] = [0; 4];
let opcode1 = ((opcode >> 4) & 0xff) as u8;
let opcode2 = (opcode & 0xf) as u8;
let m1 = m1 & 0x0f;
let ri2 = ((ri2 >> 1) & 0xffff) as u16;
enc[0] = opcode1;
enc[1] = m1 << 4 | opcode2;
enc[2..].copy_from_slice(&ri2.to_be_bytes());
enc
}
fn enc_rie_a(opcode: u16, r1: Reg, i2: u16, m3: u8) -> [u8; 6] {
let mut enc: [u8; 6] = [0; 6];
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr(r1) & 0x0f;
let m3 = m3 & 0x0f;
enc[0] = opcode1;
enc[1] = r1 << 4;
enc[2..4].copy_from_slice(&i2.to_be_bytes());
enc[4] = m3 << 4;
enc[5] = opcode2;
enc
}
fn enc_rie_d(opcode: u16, r1: Reg, r3: Reg, i2: u16) -> [u8; 6] {
let mut enc: [u8; 6] = [0; 6];
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr(r1) & 0x0f;
let r3 = machreg_to_gpr(r3) & 0x0f;
enc[0] = opcode1;
enc[1] = r1 << 4 | r3;
enc[2..4].copy_from_slice(&i2.to_be_bytes());
enc[5] = opcode2;
enc
}
fn enc_rie_g(opcode: u16, r1: Reg, i2: u16, m3: u8) -> [u8; 6] {
let mut enc: [u8; 6] = [0; 6];
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr(r1) & 0x0f;
let m3 = m3 & 0x0f;
enc[0] = opcode1;
enc[1] = r1 << 4 | m3;
enc[2..4].copy_from_slice(&i2.to_be_bytes());
enc[5] = opcode2;
enc
}
fn enc_ril_a(opcode: u16, r1: Reg, i2: u32) -> [u8; 6] {
let mut enc: [u8; 6] = [0; 6];
let opcode1 = ((opcode >> 4) & 0xff) as u8;
let opcode2 = (opcode & 0xf) as u8;
let r1 = machreg_to_gpr(r1) & 0x0f;
enc[0] = opcode1;
enc[1] = r1 << 4 | opcode2;
enc[2..].copy_from_slice(&i2.to_be_bytes());
enc
}
fn enc_ril_b(opcode: u16, r1: Reg, ri2: u32) -> [u8; 6] {
let mut enc: [u8; 6] = [0; 6];
let opcode1 = ((opcode >> 4) & 0xff) as u8;
let opcode2 = (opcode & 0xf) as u8;
let r1 = machreg_to_gpr(r1) & 0x0f;
enc[0] = opcode1;
enc[1] = r1 << 4 | opcode2;
enc[2..].copy_from_slice(&ri2.to_be_bytes());
enc
}
fn enc_ril_c(opcode: u16, m1: u8, ri2: u32) -> [u8; 6] {
let mut enc: [u8; 6] = [0; 6];
let opcode1 = ((opcode >> 4) & 0xff) as u8;
let opcode2 = (opcode & 0xf) as u8;
let m1 = m1 & 0x0f;
enc[0] = opcode1;
enc[1] = m1 << 4 | opcode2;
enc[2..].copy_from_slice(&ri2.to_be_bytes());
enc
}
fn enc_rr(opcode: u16, r1: Reg, r2: Reg) -> [u8; 2] {
let mut enc: [u8; 2] = [0; 2];
let opcode = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr_or_fpr(r1) & 0x0f;
let r2 = machreg_to_gpr_or_fpr(r2) & 0x0f;
enc[0] = opcode;
enc[1] = r1 << 4 | r2;
enc
}
fn enc_rrd(opcode: u16, r1: Reg, r2: Reg, r3: Reg) -> [u8; 4] {
let mut enc: [u8; 4] = [0; 4];
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let r1 = machreg_to_fpr(r1) & 0x0f;
let r2 = machreg_to_fpr(r2) & 0x0f;
let r3 = machreg_to_fpr(r3) & 0x0f;
enc[0] = opcode1;
enc[1] = opcode2;
enc[2] = r1 << 4;
enc[3] = r3 << 4 | r2;
enc
}
fn enc_rre(opcode: u16, r1: Reg, r2: Reg) -> [u8; 4] {
let mut enc: [u8; 4] = [0; 4];
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr_or_fpr(r1) & 0x0f;
let r2 = machreg_to_gpr_or_fpr(r2) & 0x0f;
enc[0] = opcode1;
enc[1] = opcode2;
enc[3] = r1 << 4 | r2;
enc
}
fn enc_rrf_ab(opcode: u16, r1: Reg, r2: Reg, r3: Reg, m4: u8) -> [u8; 4] {
let mut enc: [u8; 4] = [0; 4];
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr_or_fpr(r1) & 0x0f;
let r2 = machreg_to_gpr_or_fpr(r2) & 0x0f;
let r3 = machreg_to_gpr_or_fpr(r3) & 0x0f;
let m4 = m4 & 0x0f;
enc[0] = opcode1;
enc[1] = opcode2;
enc[2] = r3 << 4 | m4;
enc[3] = r1 << 4 | r2;
enc
}
fn enc_rrf_cde(opcode: u16, r1: Reg, r2: Reg, m3: u8, m4: u8) -> [u8; 4] {
let mut enc: [u8; 4] = [0; 4];
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr_or_fpr(r1) & 0x0f;
let r2 = machreg_to_gpr_or_fpr(r2) & 0x0f;
let m3 = m3 & 0x0f;
let m4 = m4 & 0x0f;
enc[0] = opcode1;
enc[1] = opcode2;
enc[2] = m3 << 4 | m4;
enc[3] = r1 << 4 | r2;
enc
}
fn enc_rs(opcode: u16, r1: Reg, r3: Reg, b2: Reg, d2: u32) -> [u8; 4] {
let opcode = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr_or_fpr(r1) & 0x0f;
let r3 = machreg_to_gpr_or_fpr(r3) & 0x0f;
let b2 = machreg_to_gpr(b2) & 0x0f;
let d2_lo = (d2 & 0xff) as u8;
let d2_hi = ((d2 >> 8) & 0x0f) as u8;
let mut enc: [u8; 4] = [0; 4];
enc[0] = opcode;
enc[1] = r1 << 4 | r3;
enc[2] = b2 << 4 | d2_hi;
enc[3] = d2_lo;
enc
}
fn enc_rsy(opcode: u16, r1: Reg, r3: Reg, b2: Reg, d2: u32) -> [u8; 6] {
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr_or_fpr(r1) & 0x0f;
let r3 = machreg_to_gpr_or_fpr(r3) & 0x0f;
let b2 = machreg_to_gpr(b2) & 0x0f;
let dl2_lo = (d2 & 0xff) as u8;
let dl2_hi = ((d2 >> 8) & 0x0f) as u8;
let dh2 = ((d2 >> 12) & 0xff) as u8;
let mut enc: [u8; 6] = [0; 6];
enc[0] = opcode1;
enc[1] = r1 << 4 | r3;
enc[2] = b2 << 4 | dl2_hi;
enc[3] = dl2_lo;
enc[4] = dh2;
enc[5] = opcode2;
enc
}
fn enc_rx(opcode: u16, r1: Reg, b2: Reg, x2: Reg, d2: u32) -> [u8; 4] {
let opcode = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr_or_fpr(r1) & 0x0f;
let b2 = machreg_to_gpr(b2) & 0x0f;
let x2 = machreg_to_gpr(x2) & 0x0f;
let d2_lo = (d2 & 0xff) as u8;
let d2_hi = ((d2 >> 8) & 0x0f) as u8;
let mut enc: [u8; 4] = [0; 4];
enc[0] = opcode;
enc[1] = r1 << 4 | x2;
enc[2] = b2 << 4 | d2_hi;
enc[3] = d2_lo;
enc
}
fn enc_rxy(opcode: u16, r1: Reg, b2: Reg, x2: Reg, d2: u32) -> [u8; 6] {
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr_or_fpr(r1) & 0x0f;
let b2 = machreg_to_gpr(b2) & 0x0f;
let x2 = machreg_to_gpr(x2) & 0x0f;
let dl2_lo = (d2 & 0xff) as u8;
let dl2_hi = ((d2 >> 8) & 0x0f) as u8;
let dh2 = ((d2 >> 12) & 0xff) as u8;
let mut enc: [u8; 6] = [0; 6];
enc[0] = opcode1;
enc[1] = r1 << 4 | x2;
enc[2] = b2 << 4 | dl2_hi;
enc[3] = dl2_lo;
enc[4] = dh2;
enc[5] = opcode2;
enc
}
fn enc_si(opcode: u16, b1: Reg, d1: u32, i2: u8) -> [u8; 4] {
let opcode = (opcode & 0xff) as u8;
let b1 = machreg_to_gpr(b1) & 0x0f;
let d1_lo = (d1 & 0xff) as u8;
let d1_hi = ((d1 >> 8) & 0x0f) as u8;
let mut enc: [u8; 4] = [0; 4];
enc[0] = opcode;
enc[1] = i2;
enc[2] = b1 << 4 | d1_hi;
enc[3] = d1_lo;
enc
}
fn enc_sil(opcode: u16, b1: Reg, d1: u32, i2: i16) -> [u8; 6] {
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let b1 = machreg_to_gpr(b1) & 0x0f;
let d1_lo = (d1 & 0xff) as u8;
let d1_hi = ((d1 >> 8) & 0x0f) as u8;
let mut enc: [u8; 6] = [0; 6];
enc[0] = opcode1;
enc[1] = opcode2;
enc[2] = b1 << 4 | d1_hi;
enc[3] = d1_lo;
enc[4..].copy_from_slice(&i2.to_be_bytes());
enc
}
fn enc_siy(opcode: u16, b1: Reg, d1: u32, i2: u8) -> [u8; 6] {
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let b1 = machreg_to_gpr(b1) & 0x0f;
let dl1_lo = (d1 & 0xff) as u8;
let dl1_hi = ((d1 >> 8) & 0x0f) as u8;
let dh1 = ((d1 >> 12) & 0xff) as u8;
let mut enc: [u8; 6] = [0; 6];
enc[0] = opcode1;
enc[1] = i2;
enc[2] = b1 << 4 | dl1_hi;
enc[3] = dl1_lo;
enc[4] = dh1;
enc[5] = opcode2;
enc
}
fn enc_vrr(opcode: u16, v1: Reg, v2: Reg, v3: Reg, m4: u8, m5: u8, m6: u8) -> [u8; 6] {
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let rxb = 0; let v1 = machreg_to_fpr(v1) & 0x0f; let v2 = machreg_to_fpr(v2) & 0x0f; let v3 = machreg_to_fpr(v3) & 0x0f; let m4 = m4 & 0x0f;
let m5 = m5 & 0x0f;
let m6 = m6 & 0x0f;
let mut enc: [u8; 6] = [0; 6];
enc[0] = opcode1;
enc[1] = v1 << 4 | v2;
enc[2] = v3 << 4;
enc[3] = m6 << 4 | m5;
enc[4] = m4 << 4 | rxb;
enc[5] = opcode2;
enc
}
fn enc_vrx(opcode: u16, v1: Reg, b2: Reg, x2: Reg, d2: u32, m3: u8) -> [u8; 6] {
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let rxb = 0; let v1 = machreg_to_fpr(v1) & 0x0f; let b2 = machreg_to_gpr(b2) & 0x0f;
let x2 = machreg_to_gpr(x2) & 0x0f;
let d2_lo = (d2 & 0xff) as u8;
let d2_hi = ((d2 >> 8) & 0x0f) as u8;
let m3 = m3 & 0x0f;
let mut enc: [u8; 6] = [0; 6];
enc[0] = opcode1;
enc[1] = v1 << 4 | x2;
enc[2] = b2 << 4 | d2_hi;
enc[3] = d2_lo;
enc[4] = m3 << 4 | rxb;
enc[5] = opcode2;
enc
}
fn put(sink: &mut MachBuffer<Inst>, enc: &[u8]) {
for byte in enc {
sink.put1(*byte);
}
}
fn put_with_trap(sink: &mut MachBuffer<Inst>, enc: &[u8], srcloc: SourceLoc, trap_code: TrapCode) {
let len = enc.len();
for i in 0..len - 1 {
sink.put1(enc[i]);
}
sink.add_trap(srcloc, trap_code);
sink.put1(enc[len - 1]);
}
fn put_with_reloc(
sink: &mut MachBuffer<Inst>,
enc: &[u8],
offset: usize,
ri2_srcloc: SourceLoc,
ri2_reloc: Reloc,
ri2_name: &ExternalName,
ri2_offset: i64,
) {
let len = enc.len();
for i in 0..offset {
sink.put1(enc[i]);
}
sink.add_reloc(ri2_srcloc, ri2_reloc, ri2_name, ri2_offset + offset as i64);
for i in offset..len {
sink.put1(enc[i]);
}
}
#[derive(Default, Clone, Debug)]
pub struct EmitState {
pub(crate) initial_sp_offset: i64,
pub(crate) virtual_sp_offset: i64,
stack_map: Option<StackMap>,
cur_srcloc: SourceLoc,
}
impl MachInstEmitState<Inst> for EmitState {
fn new(abi: &dyn ABICallee<I = Inst>) -> Self {
EmitState {
virtual_sp_offset: 0,
initial_sp_offset: 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<StackMap> {
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,
isa_flags: s390x_settings::Flags,
}
impl EmitInfo {
pub(crate) fn new(flags: settings::Flags, isa_flags: s390x_settings::Flags) -> Self {
Self { flags, isa_flags }
}
}
impl MachInstEmitInfo for EmitInfo {
fn flags(&self) -> &settings::Flags {
&self.flags
}
}
impl MachInstEmit for Inst {
type State = EmitState;
type Info = EmitInfo;
fn emit(&self, sink: &mut MachBuffer<Inst>, emit_info: &Self::Info, state: &mut EmitState) {
let matches_isa_flags = |iset_requirement: &InstructionSet| -> bool {
match iset_requirement {
InstructionSet::Base => true,
InstructionSet::MIE2 => emit_info.isa_flags.has_mie2(),
InstructionSet::VXRS_EXT2 => emit_info.isa_flags.has_vxrs_ext2(),
}
};
let isa_requirements = self.available_in_isa();
if !matches_isa_flags(&isa_requirements) {
panic!(
"Cannot emit inst '{:?}' for target; failed to match ISA requirements: {:?}",
self, isa_requirements
)
}
let mut start_off = sink.cur_offset();
match self {
&Inst::AluRRR { alu_op, rd, rn, rm } => {
let (opcode, have_rr) = match alu_op {
ALUOp::Add32 => (0xb9f8, true), ALUOp::Add64 => (0xb9e8, true), ALUOp::Sub32 => (0xb9f9, true), ALUOp::Sub64 => (0xb9e9, true), ALUOp::Mul32 => (0xb9fd, true), ALUOp::Mul64 => (0xb9ed, true), ALUOp::And32 => (0xb9f4, true), ALUOp::And64 => (0xb9e4, true), ALUOp::Orr32 => (0xb9f6, true), ALUOp::Orr64 => (0xb9e6, true), ALUOp::Xor32 => (0xb9f7, true), ALUOp::Xor64 => (0xb9e7, true), ALUOp::AndNot32 => (0xb974, false), ALUOp::AndNot64 => (0xb964, false), ALUOp::OrrNot32 => (0xb976, false), ALUOp::OrrNot64 => (0xb966, false), ALUOp::XorNot32 => (0xb977, false), ALUOp::XorNot64 => (0xb967, false), _ => unreachable!(),
};
if have_rr && rd.to_reg() == rn {
let inst = Inst::AluRR { alu_op, rd, rm };
inst.emit(sink, emit_info, state);
} else {
put(sink, &enc_rrf_ab(opcode, rd.to_reg(), rn, rm, 0));
}
}
&Inst::AluRRSImm16 {
alu_op,
rd,
rn,
imm,
} => {
if rd.to_reg() == rn {
let inst = Inst::AluRSImm16 { alu_op, rd, imm };
inst.emit(sink, emit_info, state);
} else {
let opcode = match alu_op {
ALUOp::Add32 => 0xecd8, ALUOp::Add64 => 0xecd9, _ => unreachable!(),
};
put(sink, &enc_rie_d(opcode, rd.to_reg(), rn, imm as u16));
}
}
&Inst::AluRR { alu_op, rd, rm } => {
let (opcode, is_rre) = match alu_op {
ALUOp::Add32 => (0x1a, false), ALUOp::Add64 => (0xb908, true), ALUOp::Add64Ext32 => (0xb918, true), ALUOp::Sub32 => (0x1b, false), ALUOp::Sub64 => (0xb909, true), ALUOp::Sub64Ext32 => (0xb919, true), ALUOp::Mul32 => (0xb252, true), ALUOp::Mul64 => (0xb90c, true), ALUOp::Mul64Ext32 => (0xb91c, true), ALUOp::And32 => (0x14, false), ALUOp::And64 => (0xb980, true), ALUOp::Orr32 => (0x16, false), ALUOp::Orr64 => (0xb981, true), ALUOp::Xor32 => (0x17, false), ALUOp::Xor64 => (0xb982, true), _ => unreachable!(),
};
if is_rre {
put(sink, &enc_rre(opcode, rd.to_reg(), rm));
} else {
put(sink, &enc_rr(opcode, rd.to_reg(), rm));
}
}
&Inst::AluRX {
alu_op,
rd,
ref mem,
} => {
let (opcode_rx, opcode_rxy) = match alu_op {
ALUOp::Add32 => (Some(0x5a), Some(0xe35a)), ALUOp::Add32Ext16 => (Some(0x4a), Some(0xe34a)), ALUOp::Add64 => (None, Some(0xe308)), ALUOp::Add64Ext16 => (None, Some(0xe338)), ALUOp::Add64Ext32 => (None, Some(0xe318)), ALUOp::Sub32 => (Some(0x5b), Some(0xe35b)), ALUOp::Sub32Ext16 => (Some(0x4b), Some(0xe37b)), ALUOp::Sub64 => (None, Some(0xe309)), ALUOp::Sub64Ext16 => (None, Some(0xe339)), ALUOp::Sub64Ext32 => (None, Some(0xe319)), ALUOp::Mul32 => (Some(0x71), Some(0xe351)), ALUOp::Mul32Ext16 => (Some(0x4c), Some(0xe37c)), ALUOp::Mul64 => (None, Some(0xe30c)), ALUOp::Mul64Ext16 => (None, Some(0xe33c)), ALUOp::Mul64Ext32 => (None, Some(0xe31c)), ALUOp::And32 => (Some(0x54), Some(0xe354)), ALUOp::And64 => (None, Some(0xe380)), ALUOp::Orr32 => (Some(0x56), Some(0xe356)), ALUOp::Orr64 => (None, Some(0xe381)), ALUOp::Xor32 => (Some(0x57), Some(0xe357)), ALUOp::Xor64 => (None, Some(0xe382)), _ => unreachable!(),
};
let rd = rd.to_reg();
mem_emit(
rd, mem, opcode_rx, opcode_rxy, None, true, sink, emit_info, state,
);
}
&Inst::AluRSImm16 { alu_op, rd, imm } => {
let opcode = match alu_op {
ALUOp::Add32 => 0xa7a, ALUOp::Add64 => 0xa7b, ALUOp::Mul32 => 0xa7c, ALUOp::Mul64 => 0xa7d, _ => unreachable!(),
};
put(sink, &enc_ri_a(opcode, rd.to_reg(), imm as u16));
}
&Inst::AluRSImm32 { alu_op, rd, imm } => {
let opcode = match alu_op {
ALUOp::Add32 => 0xc29, ALUOp::Add64 => 0xc28, ALUOp::Mul32 => 0xc21, ALUOp::Mul64 => 0xc20, _ => unreachable!(),
};
put(sink, &enc_ril_a(opcode, rd.to_reg(), imm as u32));
}
&Inst::AluRUImm32 { alu_op, rd, imm } => {
let opcode = match alu_op {
ALUOp::Add32 => 0xc2b, ALUOp::Add64 => 0xc2a, ALUOp::Sub32 => 0xc25, ALUOp::Sub64 => 0xc24, _ => unreachable!(),
};
put(sink, &enc_ril_a(opcode, rd.to_reg(), imm));
}
&Inst::AluRUImm16Shifted { alu_op, rd, imm } => {
let opcode = match (alu_op, imm.shift) {
(ALUOp::And32, 0) => 0xa57, (ALUOp::And32, 1) => 0xa56, (ALUOp::And64, 0) => 0xa57, (ALUOp::And64, 1) => 0xa56, (ALUOp::And64, 2) => 0xa55, (ALUOp::And64, 3) => 0xa54, (ALUOp::Orr32, 0) => 0xa5b, (ALUOp::Orr32, 1) => 0xa5a, (ALUOp::Orr64, 0) => 0xa5b, (ALUOp::Orr64, 1) => 0xa5a, (ALUOp::Orr64, 2) => 0xa59, (ALUOp::Orr64, 3) => 0xa58, _ => unreachable!(),
};
put(sink, &enc_ri_a(opcode, rd.to_reg(), imm.bits));
}
&Inst::AluRUImm32Shifted { alu_op, rd, imm } => {
let opcode = match (alu_op, imm.shift) {
(ALUOp::And32, 0) => 0xc0b, (ALUOp::And64, 0) => 0xc0b, (ALUOp::And64, 1) => 0xc0a, (ALUOp::Orr32, 0) => 0xc0d, (ALUOp::Orr64, 0) => 0xc0d, (ALUOp::Orr64, 1) => 0xc0c, (ALUOp::Xor32, 0) => 0xc07, (ALUOp::Xor64, 0) => 0xc07, (ALUOp::Xor64, 1) => 0xc06, _ => unreachable!(),
};
put(sink, &enc_ril_a(opcode, rd.to_reg(), imm.bits));
}
&Inst::SMulWide { rn, rm } => {
let opcode = 0xb9ec; put(sink, &enc_rrf_ab(opcode, gpr(0), rn, rm, 0));
}
&Inst::UMulWide { rn } => {
let opcode = 0xb986; put(sink, &enc_rre(opcode, gpr(0), rn));
}
&Inst::SDivMod32 { rn } => {
let opcode = 0xb91d; let srcloc = state.cur_srcloc();
let trap_code = TrapCode::IntegerDivisionByZero;
put_with_trap(sink, &enc_rre(opcode, gpr(0), rn), srcloc, trap_code);
}
&Inst::SDivMod64 { rn } => {
let opcode = 0xb90d; let srcloc = state.cur_srcloc();
let trap_code = TrapCode::IntegerDivisionByZero;
put_with_trap(sink, &enc_rre(opcode, gpr(0), rn), srcloc, trap_code);
}
&Inst::UDivMod32 { rn } => {
let opcode = 0xb997; let srcloc = state.cur_srcloc();
let trap_code = TrapCode::IntegerDivisionByZero;
put_with_trap(sink, &enc_rre(opcode, gpr(0), rn), srcloc, trap_code);
}
&Inst::UDivMod64 { rn } => {
let opcode = 0xb987; let srcloc = state.cur_srcloc();
let trap_code = TrapCode::IntegerDivisionByZero;
put_with_trap(sink, &enc_rre(opcode, gpr(0), rn), srcloc, trap_code);
}
&Inst::Flogr { rn } => {
let opcode = 0xb983; put(sink, &enc_rre(opcode, gpr(0), rn));
}
&Inst::ShiftRR {
shift_op,
rd,
rn,
shift_imm,
shift_reg,
} => {
let opcode = match shift_op {
ShiftOp::RotL32 => 0xeb1d, ShiftOp::RotL64 => 0xeb1c, ShiftOp::LShL32 => 0xebdf, ShiftOp::LShL64 => 0xeb0d, ShiftOp::LShR32 => 0xebde, ShiftOp::LShR64 => 0xeb0c, ShiftOp::AShR32 => 0xebdc, ShiftOp::AShR64 => 0xeb0a, };
let shift_reg = match shift_reg {
Some(reg) => reg,
None => zero_reg(),
};
put(
sink,
&enc_rsy(opcode, rd.to_reg(), rn, shift_reg, shift_imm.bits()),
);
}
&Inst::UnaryRR { op, rd, rn } => {
match op {
UnaryOp::Abs32 => {
let opcode = 0x10; put(sink, &enc_rr(opcode, rd.to_reg(), rn));
}
UnaryOp::Abs64 => {
let opcode = 0xb900; put(sink, &enc_rre(opcode, rd.to_reg(), rn));
}
UnaryOp::Abs64Ext32 => {
let opcode = 0xb910; put(sink, &enc_rre(opcode, rd.to_reg(), rn));
}
UnaryOp::Neg32 => {
let opcode = 0x13; put(sink, &enc_rr(opcode, rd.to_reg(), rn));
}
UnaryOp::Neg64 => {
let opcode = 0xb903; put(sink, &enc_rre(opcode, rd.to_reg(), rn));
}
UnaryOp::Neg64Ext32 => {
let opcode = 0xb913; put(sink, &enc_rre(opcode, rd.to_reg(), rn));
}
UnaryOp::PopcntByte => {
let opcode = 0xb9e1; put(sink, &enc_rrf_cde(opcode, rd.to_reg(), rn, 0, 0));
}
UnaryOp::PopcntReg => {
let opcode = 0xb9e1; put(sink, &enc_rrf_cde(opcode, rd.to_reg(), rn, 8, 0));
}
}
}
&Inst::Extend {
rd,
rn,
signed,
from_bits,
to_bits,
} => {
let opcode = match (signed, from_bits, to_bits) {
(_, 1, 32) => 0xb926, (_, 1, 64) => 0xb906, (false, 8, 32) => 0xb994, (false, 8, 64) => 0xb984, (true, 8, 32) => 0xb926, (true, 8, 64) => 0xb906, (false, 16, 32) => 0xb995, (false, 16, 64) => 0xb985, (true, 16, 32) => 0xb927, (true, 16, 64) => 0xb907, (false, 32, 64) => 0xb916, (true, 32, 64) => 0xb914, _ => panic!(
"Unsupported extend combination: signed = {}, from_bits = {}, to_bits = {}",
signed, from_bits, to_bits
),
};
put(sink, &enc_rre(opcode, rd.to_reg(), rn));
}
&Inst::CmpRR { op, rn, rm } => {
let (opcode, is_rre) = match op {
CmpOp::CmpS32 => (0x19, false), CmpOp::CmpS64 => (0xb920, true), CmpOp::CmpS64Ext32 => (0xb930, true), CmpOp::CmpL32 => (0x15, false), CmpOp::CmpL64 => (0xb921, true), CmpOp::CmpL64Ext32 => (0xb931, true), _ => unreachable!(),
};
if is_rre {
put(sink, &enc_rre(opcode, rn, rm));
} else {
put(sink, &enc_rr(opcode, rn, rm));
}
}
&Inst::CmpRX { op, rn, ref mem } => {
let (opcode_rx, opcode_rxy, opcode_ril) = match op {
CmpOp::CmpS32 => (Some(0x59), Some(0xe359), Some(0xc6d)), CmpOp::CmpS32Ext16 => (Some(0x49), Some(0xe379), Some(0xc65)), CmpOp::CmpS64 => (None, Some(0xe320), Some(0xc68)), CmpOp::CmpS64Ext16 => (None, Some(0xe334), Some(0xc64)), CmpOp::CmpS64Ext32 => (None, Some(0xe330), Some(0xc6c)), CmpOp::CmpL32 => (Some(0x55), Some(0xe355), Some(0xc6f)), CmpOp::CmpL32Ext16 => (None, None, Some(0xc67)), CmpOp::CmpL64 => (None, Some(0xe321), Some(0xc6a)), CmpOp::CmpL64Ext16 => (None, None, Some(0xc66)), CmpOp::CmpL64Ext32 => (None, Some(0xe331), Some(0xc6e)), };
mem_emit(
rn, mem, opcode_rx, opcode_rxy, opcode_ril, true, sink, emit_info, state,
);
}
&Inst::CmpRSImm16 { op, rn, imm } => {
let opcode = match op {
CmpOp::CmpS32 => 0xa7e, CmpOp::CmpS64 => 0xa7f, _ => unreachable!(),
};
put(sink, &enc_ri_a(opcode, rn, imm as u16));
}
&Inst::CmpRSImm32 { op, rn, imm } => {
let opcode = match op {
CmpOp::CmpS32 => 0xc2d, CmpOp::CmpS64 => 0xc2c, _ => unreachable!(),
};
put(sink, &enc_ril_a(opcode, rn, imm as u32));
}
&Inst::CmpRUImm32 { op, rn, imm } => {
let opcode = match op {
CmpOp::CmpL32 => 0xc2f, CmpOp::CmpL64 => 0xc2e, _ => unreachable!(),
};
put(sink, &enc_ril_a(opcode, rn, imm));
}
&Inst::CmpTrapRR {
op,
rn,
rm,
cond,
trap_code,
} => {
let opcode = match op {
CmpOp::CmpS32 => 0xb972, CmpOp::CmpS64 => 0xb960, CmpOp::CmpL32 => 0xb973, CmpOp::CmpL64 => 0xb961, _ => unreachable!(),
};
let srcloc = state.cur_srcloc();
put_with_trap(
sink,
&enc_rrf_cde(opcode, rn, rm, cond.bits(), 0),
srcloc,
trap_code,
);
}
&Inst::CmpTrapRSImm16 {
op,
rn,
imm,
cond,
trap_code,
} => {
let opcode = match op {
CmpOp::CmpS32 => 0xec72, CmpOp::CmpS64 => 0xec70, _ => unreachable!(),
};
let srcloc = state.cur_srcloc();
put_with_trap(
sink,
&enc_rie_a(opcode, rn, imm as u16, cond.bits()),
srcloc,
trap_code,
);
}
&Inst::CmpTrapRUImm16 {
op,
rn,
imm,
cond,
trap_code,
} => {
let opcode = match op {
CmpOp::CmpL32 => 0xec73, CmpOp::CmpL64 => 0xec71, _ => unreachable!(),
};
let srcloc = state.cur_srcloc();
put_with_trap(
sink,
&enc_rie_a(opcode, rn, imm, cond.bits()),
srcloc,
trap_code,
);
}
&Inst::AtomicRmw {
alu_op,
rd,
rn,
ref mem,
} => {
let opcode = match alu_op {
ALUOp::Add32 => 0xebf8, ALUOp::Add64 => 0xebe8, ALUOp::And32 => 0xebf4, ALUOp::And64 => 0xebe4, ALUOp::Orr32 => 0xebf6, ALUOp::Orr64 => 0xebe6, ALUOp::Xor32 => 0xebf7, ALUOp::Xor64 => 0xebe7, _ => unreachable!(),
};
let rd = rd.to_reg();
mem_rs_emit(
rd,
rn,
mem,
None,
Some(opcode),
true,
sink,
emit_info,
state,
);
}
&Inst::AtomicCas32 { rd, rn, ref mem } | &Inst::AtomicCas64 { rd, rn, ref mem } => {
let (opcode_rs, opcode_rsy) = match self {
&Inst::AtomicCas32 { .. } => (Some(0xba), Some(0xeb14)), &Inst::AtomicCas64 { .. } => (None, Some(0xeb30)), _ => unreachable!(),
};
let rd = rd.to_reg();
mem_rs_emit(
rd, rn, mem, opcode_rs, opcode_rsy, true, sink, emit_info, state,
);
}
&Inst::Fence => {
put(sink, &enc_e(0x07e0));
}
&Inst::Load32 { rd, ref mem }
| &Inst::Load32ZExt8 { rd, ref mem }
| &Inst::Load32SExt8 { rd, ref mem }
| &Inst::Load32ZExt16 { rd, ref mem }
| &Inst::Load32SExt16 { rd, ref mem }
| &Inst::Load64 { rd, ref mem }
| &Inst::Load64ZExt8 { rd, ref mem }
| &Inst::Load64SExt8 { rd, ref mem }
| &Inst::Load64ZExt16 { rd, ref mem }
| &Inst::Load64SExt16 { rd, ref mem }
| &Inst::Load64ZExt32 { rd, ref mem }
| &Inst::Load64SExt32 { rd, ref mem }
| &Inst::LoadRev16 { rd, ref mem }
| &Inst::LoadRev32 { rd, ref mem }
| &Inst::LoadRev64 { rd, ref mem }
| &Inst::FpuLoad32 { rd, ref mem }
| &Inst::FpuLoad64 { rd, ref mem } => {
let (opcode_rx, opcode_rxy, opcode_ril) = match self {
&Inst::Load32 { .. } => (Some(0x58), Some(0xe358), Some(0xc4d)), &Inst::Load32ZExt8 { .. } => (None, Some(0xe394), None), &Inst::Load32SExt8 { .. } => (None, Some(0xe376), None), &Inst::Load32ZExt16 { .. } => (None, Some(0xe395), Some(0xc42)), &Inst::Load32SExt16 { .. } => (Some(0x48), Some(0xe378), Some(0xc45)), &Inst::Load64 { .. } => (None, Some(0xe304), Some(0xc48)), &Inst::Load64ZExt8 { .. } => (None, Some(0xe390), None), &Inst::Load64SExt8 { .. } => (None, Some(0xe377), None), &Inst::Load64ZExt16 { .. } => (None, Some(0xe391), Some(0xc46)), &Inst::Load64SExt16 { .. } => (None, Some(0xe315), Some(0xc44)), &Inst::Load64ZExt32 { .. } => (None, Some(0xe316), Some(0xc4e)), &Inst::Load64SExt32 { .. } => (None, Some(0xe314), Some(0xc4c)), &Inst::LoadRev16 { .. } => (None, Some(0xe31f), None), &Inst::LoadRev32 { .. } => (None, Some(0xe31e), None), &Inst::LoadRev64 { .. } => (None, Some(0xe30f), None), &Inst::FpuLoad32 { .. } => (Some(0x78), Some(0xed64), None), &Inst::FpuLoad64 { .. } => (Some(0x68), Some(0xed65), None), _ => unreachable!(),
};
let rd = rd.to_reg();
mem_emit(
rd, mem, opcode_rx, opcode_rxy, opcode_ril, true, sink, emit_info, state,
);
}
&Inst::FpuLoadRev32 { rd, ref mem } | &Inst::FpuLoadRev64 { rd, ref mem } => {
let opcode = match self {
&Inst::FpuLoadRev32 { .. } => 0xe603, &Inst::FpuLoadRev64 { .. } => 0xe602, _ => unreachable!(),
};
let (mem_insts, mem) = mem_finalize(mem, state, true, false, false, true);
for inst in mem_insts.into_iter() {
inst.emit(sink, emit_info, state);
}
let srcloc = state.cur_srcloc();
if srcloc != SourceLoc::default() && mem.can_trap() {
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
}
match &mem {
&MemArg::BXD12 {
base, index, disp, ..
} => {
put(
sink,
&enc_vrx(opcode, rd.to_reg(), base, index, disp.bits(), 0),
);
}
_ => unreachable!(),
}
}
&Inst::Store8 { rd, ref mem }
| &Inst::Store16 { rd, ref mem }
| &Inst::Store32 { rd, ref mem }
| &Inst::Store64 { rd, ref mem }
| &Inst::StoreRev16 { rd, ref mem }
| &Inst::StoreRev32 { rd, ref mem }
| &Inst::StoreRev64 { rd, ref mem }
| &Inst::FpuStore32 { rd, ref mem }
| &Inst::FpuStore64 { rd, ref mem } => {
let (opcode_rx, opcode_rxy, opcode_ril) = match self {
&Inst::Store8 { .. } => (Some(0x42), Some(0xe372), None), &Inst::Store16 { .. } => (Some(0x40), Some(0xe370), Some(0xc47)), &Inst::Store32 { .. } => (Some(0x50), Some(0xe350), Some(0xc4f)), &Inst::Store64 { .. } => (None, Some(0xe324), Some(0xc4b)), &Inst::StoreRev16 { .. } => (None, Some(0xe33f), None), &Inst::StoreRev32 { .. } => (None, Some(0xe33e), None), &Inst::StoreRev64 { .. } => (None, Some(0xe32f), None), &Inst::FpuStore32 { .. } => (Some(0x70), Some(0xed66), None), &Inst::FpuStore64 { .. } => (Some(0x60), Some(0xed67), None), _ => unreachable!(),
};
mem_emit(
rd, mem, opcode_rx, opcode_rxy, opcode_ril, true, sink, emit_info, state,
);
}
&Inst::StoreImm8 { imm, ref mem } => {
let opcode_si = 0x92; let opcode_siy = 0xeb52; mem_imm8_emit(
imm, mem, opcode_si, opcode_siy, true, sink, emit_info, state,
);
}
&Inst::StoreImm16 { imm, ref mem }
| &Inst::StoreImm32SExt16 { imm, ref mem }
| &Inst::StoreImm64SExt16 { imm, ref mem } => {
let opcode = match self {
&Inst::StoreImm16 { .. } => 0xe544, &Inst::StoreImm32SExt16 { .. } => 0xe54c, &Inst::StoreImm64SExt16 { .. } => 0xe548, _ => unreachable!(),
};
mem_imm16_emit(imm, mem, opcode, true, sink, emit_info, state);
}
&Inst::FpuStoreRev32 { rd, ref mem } | &Inst::FpuStoreRev64 { rd, ref mem } => {
let opcode = match self {
&Inst::FpuStoreRev32 { .. } => 0xe60b, &Inst::FpuStoreRev64 { .. } => 0xe60a, _ => unreachable!(),
};
let (mem_insts, mem) = mem_finalize(mem, state, true, false, false, true);
for inst in mem_insts.into_iter() {
inst.emit(sink, emit_info, state);
}
let srcloc = state.cur_srcloc();
if srcloc != SourceLoc::default() && mem.can_trap() {
sink.add_trap(srcloc, TrapCode::HeapOutOfBounds);
}
match &mem {
&MemArg::BXD12 {
base, index, disp, ..
} => {
put(sink, &enc_vrx(opcode, rd, base, index, disp.bits(), 0));
}
_ => unreachable!(),
}
}
&Inst::LoadMultiple64 {
rt,
rt2,
addr_reg,
addr_off,
} => {
let opcode = 0xeb04; let rt = rt.to_reg();
let rt2 = rt2.to_reg();
put(sink, &enc_rsy(opcode, rt, rt2, addr_reg, addr_off.bits()));
}
&Inst::StoreMultiple64 {
rt,
rt2,
addr_reg,
addr_off,
} => {
let opcode = 0xeb24; put(sink, &enc_rsy(opcode, rt, rt2, addr_reg, addr_off.bits()));
}
&Inst::LoadAddr { rd, ref mem } => {
let opcode_rx = Some(0x41); let opcode_rxy = Some(0xe371); let opcode_ril = Some(0xc00); let rd = rd.to_reg();
mem_emit(
rd, mem, opcode_rx, opcode_rxy, opcode_ril, false, sink, emit_info, state,
);
}
&Inst::Mov64 { rd, rm } => {
let opcode = 0xb904; put(sink, &enc_rre(opcode, rd.to_reg(), rm));
}
&Inst::Mov32 { rd, rm } => {
let opcode = 0x18; put(sink, &enc_rr(opcode, rd.to_reg(), rm));
}
&Inst::Mov32Imm { rd, imm } => {
let opcode = 0xc09; put(sink, &enc_ril_a(opcode, rd.to_reg(), imm));
}
&Inst::Mov32SImm16 { rd, imm } => {
let opcode = 0xa78; put(sink, &enc_ri_a(opcode, rd.to_reg(), imm as u16));
}
&Inst::Mov64SImm16 { rd, imm } => {
let opcode = 0xa79; put(sink, &enc_ri_a(opcode, rd.to_reg(), imm as u16));
}
&Inst::Mov64SImm32 { rd, imm } => {
let opcode = 0xc01; put(sink, &enc_ril_a(opcode, rd.to_reg(), imm as u32));
}
&Inst::CMov32 { rd, cond, rm } => {
let opcode = 0xb9f2; put(sink, &enc_rrf_cde(opcode, rd.to_reg(), rm, cond.bits(), 0));
}
&Inst::CMov64 { rd, cond, rm } => {
let opcode = 0xb9e2; put(sink, &enc_rrf_cde(opcode, rd.to_reg(), rm, cond.bits(), 0));
}
&Inst::CMov32SImm16 { rd, cond, imm } => {
let opcode = 0xec42; put(
sink,
&enc_rie_g(opcode, rd.to_reg(), imm as u16, cond.bits()),
);
}
&Inst::CMov64SImm16 { rd, cond, imm } => {
let opcode = 0xec46; put(
sink,
&enc_rie_g(opcode, rd.to_reg(), imm as u16, cond.bits()),
);
}
&Inst::Mov64UImm16Shifted { rd, imm } => {
let opcode = match imm.shift {
0 => 0xa5f, 1 => 0xa5e, 2 => 0xa5d, 3 => 0xa5c, _ => unreachable!(),
};
put(sink, &enc_ri_a(opcode, rd.to_reg(), imm.bits));
}
&Inst::Mov64UImm32Shifted { rd, imm } => {
let opcode = match imm.shift {
0 => 0xc0f, 1 => 0xc0e, _ => unreachable!(),
};
put(sink, &enc_ril_a(opcode, rd.to_reg(), imm.bits));
}
&Inst::Insert64UImm16Shifted { rd, imm } => {
let opcode = match imm.shift {
0 => 0xa53, 1 => 0xa52, 2 => 0xa51, 3 => 0xa50, _ => unreachable!(),
};
put(sink, &enc_ri_a(opcode, rd.to_reg(), imm.bits));
}
&Inst::Insert64UImm32Shifted { rd, imm } => {
let opcode = match imm.shift {
0 => 0xc09, 1 => 0xc08, _ => unreachable!(),
};
put(sink, &enc_ril_a(opcode, rd.to_reg(), imm.bits));
}
&Inst::LoadExtNameFar {
rd,
ref name,
offset,
} => {
let opcode = 0xa75; let srcloc = state.cur_srcloc();
let reg = writable_spilltmp_reg().to_reg();
put(sink, &enc_ri_b(opcode, reg, 12));
sink.add_reloc(srcloc, Reloc::Abs8, name, offset);
if emit_info.flags().emit_all_ones_funcaddrs() {
sink.put8(u64::max_value());
} else {
sink.put8(0);
}
let inst = Inst::Load64 {
rd,
mem: MemArg::reg(reg, MemFlags::trusted()),
};
inst.emit(sink, emit_info, state);
}
&Inst::FpuMove32 { rd, rn } => {
let opcode = 0x38; put(sink, &enc_rr(opcode, rd.to_reg(), rn));
}
&Inst::FpuMove64 { rd, rn } => {
let opcode = 0x28; put(sink, &enc_rr(opcode, rd.to_reg(), rn));
}
&Inst::FpuCMov32 { rd, cond, rm } => {
let opcode = 0xa74; put(sink, &enc_ri_c(opcode, cond.invert().bits(), 4 + 2));
let opcode = 0x38; put(sink, &enc_rr(opcode, rd.to_reg(), rm));
}
&Inst::FpuCMov64 { rd, cond, rm } => {
let opcode = 0xa74; put(sink, &enc_ri_c(opcode, cond.invert().bits(), 4 + 2));
let opcode = 0x28; put(sink, &enc_rr(opcode, rd.to_reg(), rm));
}
&Inst::MovToFpr { rd, rn } => {
let opcode = 0xb3c1; put(sink, &enc_rre(opcode, rd.to_reg(), rn));
}
&Inst::MovFromFpr { rd, rn } => {
let opcode = 0xb3cd; put(sink, &enc_rre(opcode, rd.to_reg(), rn));
}
&Inst::LoadFpuConst32 { rd, const_data } => {
let opcode = 0xa75; let reg = writable_spilltmp_reg().to_reg();
put(sink, &enc_ri_b(opcode, reg, 8));
sink.put4(const_data.to_bits().swap_bytes());
let inst = Inst::FpuLoad32 {
rd,
mem: MemArg::reg(reg, MemFlags::trusted()),
};
inst.emit(sink, emit_info, state);
}
&Inst::LoadFpuConst64 { rd, const_data } => {
let opcode = 0xa75; let reg = writable_spilltmp_reg().to_reg();
put(sink, &enc_ri_b(opcode, reg, 12));
sink.put8(const_data.to_bits().swap_bytes());
let inst = Inst::FpuLoad64 {
rd,
mem: MemArg::reg(reg, MemFlags::trusted()),
};
inst.emit(sink, emit_info, state);
}
&Inst::FpuCopysign { rd, rn, rm } => {
let opcode = 0xb372; put(sink, &enc_rrf_ab(opcode, rd.to_reg(), rn, rm, 0));
}
&Inst::FpuRR { fpu_op, rd, rn } => {
let opcode = match fpu_op {
FPUOp1::Abs32 => 0xb300, FPUOp1::Abs64 => 0xb310, FPUOp1::Neg32 => 0xb303, FPUOp1::Neg64 => 0xb313, FPUOp1::NegAbs32 => 0xb301, FPUOp1::NegAbs64 => 0xb311, FPUOp1::Sqrt32 => 0xb314, FPUOp1::Sqrt64 => 0xb315, FPUOp1::Cvt32To64 => 0xb304, FPUOp1::Cvt64To32 => 0xb344, };
put(sink, &enc_rre(opcode, rd.to_reg(), rn));
}
&Inst::FpuRRR { fpu_op, rd, rm } => {
let opcode = match fpu_op {
FPUOp2::Add32 => 0xb30a, FPUOp2::Add64 => 0xb31a, FPUOp2::Sub32 => 0xb30b, FPUOp2::Sub64 => 0xb31b, FPUOp2::Mul32 => 0xb317, FPUOp2::Mul64 => 0xb31c, FPUOp2::Div32 => 0xb30d, FPUOp2::Div64 => 0xb31d, _ => unimplemented!(),
};
put(sink, &enc_rre(opcode, rd.to_reg(), rm));
}
&Inst::FpuRRRR { fpu_op, rd, rn, rm } => {
let opcode = match fpu_op {
FPUOp3::MAdd32 => 0xb30e, FPUOp3::MAdd64 => 0xb31e, FPUOp3::MSub32 => 0xb30f, FPUOp3::MSub64 => 0xb31f, };
put(sink, &enc_rrd(opcode, rd.to_reg(), rm, rn));
}
&Inst::FpuToInt { op, rd, rn } => {
let opcode = match op {
FpuToIntOp::F32ToI32 => 0xb398, FpuToIntOp::F32ToU32 => 0xb39c, FpuToIntOp::F32ToI64 => 0xb3a8, FpuToIntOp::F32ToU64 => 0xb3ac, FpuToIntOp::F64ToI32 => 0xb399, FpuToIntOp::F64ToU32 => 0xb39d, FpuToIntOp::F64ToI64 => 0xb3a9, FpuToIntOp::F64ToU64 => 0xb3ad, };
put(sink, &enc_rrf_cde(opcode, rd.to_reg(), rn, 5, 0));
}
&Inst::IntToFpu { op, rd, rn } => {
let opcode = match op {
IntToFpuOp::I32ToF32 => 0xb394, IntToFpuOp::U32ToF32 => 0xb390, IntToFpuOp::I64ToF32 => 0xb3a4, IntToFpuOp::U64ToF32 => 0xb3a0, IntToFpuOp::I32ToF64 => 0xb395, IntToFpuOp::U32ToF64 => 0xb391, IntToFpuOp::I64ToF64 => 0xb3a5, IntToFpuOp::U64ToF64 => 0xb3a1, };
put(sink, &enc_rrf_cde(opcode, rd.to_reg(), rn, 0, 0));
}
&Inst::FpuRound { op, rd, rn } => {
let (opcode, m3) = match op {
FpuRoundMode::Minus32 => (0xb357, 7), FpuRoundMode::Minus64 => (0xb35f, 7), FpuRoundMode::Plus32 => (0xb357, 6), FpuRoundMode::Plus64 => (0xb35f, 6), FpuRoundMode::Zero32 => (0xb357, 5), FpuRoundMode::Zero64 => (0xb35f, 5), FpuRoundMode::Nearest32 => (0xb357, 4), FpuRoundMode::Nearest64 => (0xb35f, 4), };
put(sink, &enc_rrf_cde(opcode, rd.to_reg(), rn, m3, 0));
}
&Inst::FpuVecRRR { fpu_op, rd, rn, rm } => {
let (opcode, m4) = match fpu_op {
FPUOp2::Max32 => (0xe7ef, 2), FPUOp2::Max64 => (0xe7ef, 3), FPUOp2::Min32 => (0xe7ee, 2), FPUOp2::Min64 => (0xe7ee, 3), _ => unimplemented!(),
};
put(sink, &enc_vrr(opcode, rd.to_reg(), rn, rm, m4, 8, 1));
}
&Inst::FpuCmp32 { rn, rm } => {
let opcode = 0xb309; put(sink, &enc_rre(opcode, rn, rm));
}
&Inst::FpuCmp64 { rn, rm } => {
let opcode = 0xb319; put(sink, &enc_rre(opcode, rn, rm));
}
&Inst::Call { link, ref info } => {
let opcode = 0xc05; let reloc = Reloc::S390xPCRel32Dbl;
let srcloc = state.cur_srcloc();
if let Some(s) = state.take_stack_map() {
sink.add_stack_map(StackMapExtent::UpcomingBytes(6), s);
}
put_with_reloc(
sink,
&enc_ril_b(opcode, link.to_reg(), 0),
2,
srcloc,
reloc,
&info.dest,
0,
);
if info.opcode.is_call() {
sink.add_call_site(srcloc, info.opcode);
}
}
&Inst::CallInd { link, ref info } => {
let opcode = 0x0d; let srcloc = state.cur_srcloc();
if let Some(s) = state.take_stack_map() {
sink.add_stack_map(StackMapExtent::UpcomingBytes(2), s);
}
put(sink, &enc_rr(opcode, link.to_reg(), info.rn));
if info.opcode.is_call() {
sink.add_call_site(srcloc, info.opcode);
}
}
&Inst::Ret { link } => {
let opcode = 0x07; put(sink, &enc_rr(opcode, gpr(15), link));
}
&Inst::EpiloguePlaceholder => {
}
&Inst::Jump { ref dest } => {
let off = sink.cur_offset();
if let Some(l) = dest.as_label() {
sink.use_label_at_offset(off, l, LabelUse::BranchRIL);
sink.add_uncond_branch(off, off + 6, l);
}
let opcode = 0xc04; put(sink, &enc_ril_c(opcode, 15, dest.as_ril_offset_or_zero()));
}
&Inst::IndirectBr { rn, .. } => {
let opcode = 0x07; put(sink, &enc_rr(opcode, gpr(15), rn));
}
&Inst::CondBr {
ref taken,
ref not_taken,
cond,
} => {
let opcode = 0xc04;
let cond_off = sink.cur_offset();
if let Some(l) = taken.as_label() {
sink.use_label_at_offset(cond_off, l, LabelUse::BranchRIL);
let inverted = &enc_ril_c(opcode, cond.invert().bits(), 0);
sink.add_cond_branch(cond_off, cond_off + 6, l, inverted);
}
put(
sink,
&enc_ril_c(opcode, cond.bits(), taken.as_ril_offset_or_zero()),
);
let uncond_off = sink.cur_offset();
if let Some(l) = not_taken.as_label() {
sink.use_label_at_offset(uncond_off, l, LabelUse::BranchRIL);
sink.add_uncond_branch(uncond_off, uncond_off + 6, l);
}
put(
sink,
&enc_ril_c(opcode, 15, not_taken.as_ril_offset_or_zero()),
);
}
&Inst::OneWayCondBr { ref target, cond } => {
let opcode = 0xc04; if let Some(l) = target.as_label() {
sink.use_label_at_offset(sink.cur_offset(), l, LabelUse::BranchRIL);
}
put(
sink,
&enc_ril_c(opcode, cond.bits(), target.as_ril_offset_or_zero()),
);
}
&Inst::Nop0 => {}
&Inst::Nop2 => {
put(sink, &enc_e(0x0707));
}
&Inst::Debugtrap => {
put(sink, &enc_e(0x0001));
}
&Inst::Trap { trap_code } => {
if let Some(s) = state.take_stack_map() {
sink.add_stack_map(StackMapExtent::UpcomingBytes(2), s);
}
let srcloc = state.cur_srcloc();
put_with_trap(sink, &enc_e(0x0000), srcloc, trap_code);
}
&Inst::TrapIf { cond, trap_code } => {
let opcode = 0xa74; put(sink, &enc_ri_c(opcode, cond.invert().bits(), 4 + 2));
if let Some(s) = state.take_stack_map() {
sink.add_stack_map(StackMapExtent::UpcomingBytes(2), s);
}
let srcloc = state.cur_srcloc();
put_with_trap(sink, &enc_e(0x0000), srcloc, trap_code);
}
&Inst::JTSequence {
ridx,
rtmp1,
rtmp2,
ref info,
..
} => {
let table_label = sink.get_label();
let inst = Inst::CmpRUImm32 {
op: CmpOp::CmpL64,
rn: ridx,
imm: info.targets.len() as u32,
};
inst.emit(sink, emit_info, state);
let inst = Inst::OneWayCondBr {
target: info.default_target,
cond: Cond::from_intcc(IntCC::UnsignedGreaterThanOrEqual),
};
inst.emit(sink, emit_info, state);
let inst = Inst::ShiftRR {
shift_op: ShiftOp::LShL64,
rd: rtmp2,
rn: ridx,
shift_imm: SImm20::maybe_from_i64(2).unwrap(),
shift_reg: None,
};
inst.emit(sink, emit_info, state);
let inst = Inst::LoadAddr {
rd: rtmp1,
mem: MemArg::Label {
target: BranchTarget::Label(table_label),
},
};
inst.emit(sink, emit_info, state);
let inst = Inst::Load64SExt32 {
rd: rtmp2,
mem: MemArg::reg_plus_reg(rtmp1.to_reg(), rtmp2.to_reg(), MemFlags::trusted()),
};
inst.emit(sink, emit_info, state);
let inst = Inst::AluRRR {
alu_op: ALUOp::Add64,
rd: rtmp1,
rn: rtmp1.to_reg(),
rm: rtmp2.to_reg(),
};
inst.emit(sink, emit_info, state);
let inst = Inst::IndirectBr {
rn: rtmp1.to_reg(),
targets: vec![],
};
inst.emit(sink, emit_info, state);
sink.bind_label(table_label);
let jt_off = sink.cur_offset();
for &target in info.targets.iter() {
let word_off = sink.cur_offset();
let off_into_table = word_off - jt_off;
sink.use_label_at_offset(
word_off,
target.as_label().unwrap(),
LabelUse::PCRel32,
);
sink.put4(off_into_table.swap_bytes());
}
start_off = sink.cur_offset();
}
&Inst::VirtualSPOffsetAdj { offset } => {
log::trace!(
"virtual sp offset adjusted by {} -> {}",
offset,
state.virtual_sp_offset + offset
);
state.virtual_sp_offset += offset;
}
&Inst::ValueLabelMarker { .. } => {
}
&Inst::Unwind { ref inst } => {
sink.add_unwind(inst.clone());
}
}
let end_off = sink.cur_offset();
debug_assert!((end_off - start_off) <= Inst::worst_case_size());
state.clear_post_insn();
}
fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, state: &mut EmitState) -> String {
self.print_with_state(mb_rru, state)
}
}