blob: d69fe2d60c7899cad8eae94fa329ba25b773ed4e [file] [log] [blame]
//! S390x ISA definitions: instruction arguments.
use crate::ir::condcodes::{FloatCC, IntCC};
use crate::ir::MemFlags;
use crate::isa::s390x::inst::*;
use crate::machinst::MachLabel;
use crate::machinst::{PrettyPrint, Reg};
use std::string::String;
//=============================================================================
// Instruction sub-components (memory addresses): definitions
/// A memory argument to load/store, encapsulating the possible addressing modes.
#[derive(Clone, Debug)]
pub enum MemArg {
//
// Real IBM Z addressing modes:
//
/// Base register, index register, and 12-bit unsigned displacement.
BXD12 {
base: Reg,
index: Reg,
disp: UImm12,
flags: MemFlags,
},
/// Base register, index register, and 20-bit signed displacement.
BXD20 {
base: Reg,
index: Reg,
disp: SImm20,
flags: MemFlags,
},
/// PC-relative Reference to a label.
Label { target: MachLabel },
/// PC-relative Reference to a near symbol.
Symbol {
name: Box<ExternalName>,
offset: i32,
flags: MemFlags,
},
//
// Virtual addressing modes that are lowered at emission time:
//
/// Arbitrary offset from a register. Converted to generation of large
/// offsets with multiple instructions as necessary during code emission.
RegOffset { reg: Reg, off: i64, flags: MemFlags },
/// Offset from the stack pointer at function entry.
InitialSPOffset { off: i64 },
/// Offset from the "nominal stack pointer", which is where the real SP is
/// just after stack and spill slots are allocated in the function prologue.
/// At emission time, this is converted to `SPOffset` with a fixup added to
/// the offset constant. The fixup is a running value that is tracked as
/// emission iterates through instructions in linear order, and can be
/// adjusted up and down with [Inst::VirtualSPOffsetAdj].
///
/// The standard ABI is in charge of handling this (by emitting the
/// adjustment meta-instructions). It maintains the invariant that "nominal
/// SP" is where the actual SP is after the function prologue and before
/// clobber pushes. See the diagram in the documentation for
/// [crate::isa::s390x::abi](the ABI module) for more details.
NominalSPOffset { off: i64 },
}
impl MemArg {
/// Memory reference using an address in a register.
pub fn reg(reg: Reg, flags: MemFlags) -> MemArg {
MemArg::BXD12 {
base: reg,
index: zero_reg(),
disp: UImm12::zero(),
flags,
}
}
/// Memory reference using the sum of two registers as an address.
pub fn reg_plus_reg(reg1: Reg, reg2: Reg, flags: MemFlags) -> MemArg {
MemArg::BXD12 {
base: reg1,
index: reg2,
disp: UImm12::zero(),
flags,
}
}
/// Memory reference using the sum of a register an an offset as address.
pub fn reg_plus_off(reg: Reg, off: i64, flags: MemFlags) -> MemArg {
MemArg::RegOffset { reg, off, flags }
}
pub(crate) fn get_flags(&self) -> MemFlags {
match self {
MemArg::BXD12 { flags, .. } => *flags,
MemArg::BXD20 { flags, .. } => *flags,
MemArg::RegOffset { flags, .. } => *flags,
MemArg::Label { .. } => MemFlags::trusted(),
MemArg::Symbol { flags, .. } => *flags,
MemArg::InitialSPOffset { .. } => MemFlags::trusted(),
MemArg::NominalSPOffset { .. } => MemFlags::trusted(),
}
}
pub(crate) fn can_trap(&self) -> bool {
!self.get_flags().notrap()
}
/// Edit registers with allocations.
pub fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self {
match self {
&MemArg::BXD12 {
base,
index,
disp,
flags,
} => MemArg::BXD12 {
base: allocs.next(base),
index: allocs.next(index),
disp,
flags,
},
&MemArg::BXD20 {
base,
index,
disp,
flags,
} => MemArg::BXD20 {
base: allocs.next(base),
index: allocs.next(index),
disp,
flags,
},
&MemArg::RegOffset { reg, off, flags } => MemArg::RegOffset {
reg: allocs.next(reg),
off,
flags,
},
x => x.clone(),
}
}
}
/// A memory argument for an instruction with two memory operands.
/// We cannot use two instances of MemArg, because we do not have
/// two free temp registers that would be needed to reload two
/// addresses in the general case. Also, two copies of MemArg would
/// increase the size of Inst beyond its current limit. Use this
/// simplified form instead that never needs any reloads, and suffices
/// for all current users.
#[derive(Clone, Debug)]
pub struct MemArgPair {
pub base: Reg,
pub disp: UImm12,
pub flags: MemFlags,
}
impl MemArgPair {
/// Convert a MemArg to a MemArgPair if possible.
pub fn maybe_from_memarg(mem: &MemArg) -> Option<MemArgPair> {
match mem {
&MemArg::BXD12 {
base,
index,
disp,
flags,
} => {
if index != zero_reg() {
None
} else {
Some(MemArgPair { base, disp, flags })
}
}
&MemArg::RegOffset { reg, off, flags } => {
if off < 0 {
None
} else {
let disp = UImm12::maybe_from_u64(off as u64)?;
Some(MemArgPair {
base: reg,
disp,
flags,
})
}
}
_ => None,
}
}
pub(crate) fn can_trap(&self) -> bool {
!self.flags.notrap()
}
/// Edit registers with allocations.
pub fn with_allocs(&self, allocs: &mut AllocationConsumer<'_>) -> Self {
MemArgPair {
base: allocs.next(self.base),
disp: self.disp,
flags: self.flags,
}
}
}
//=============================================================================
// Instruction sub-components (conditions, branches and branch targets):
// definitions
/// Condition for conditional branches.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Cond {
mask: u8,
}
impl Cond {
pub fn from_mask(mask: u8) -> Cond {
assert!(mask >= 1 && mask <= 14);
Cond { mask }
}
pub fn from_intcc(cc: IntCC) -> Cond {
let mask = match cc {
IntCC::Equal => 8,
IntCC::NotEqual => 4 | 2,
IntCC::SignedGreaterThanOrEqual => 8 | 2,
IntCC::SignedGreaterThan => 2,
IntCC::SignedLessThanOrEqual => 8 | 4,
IntCC::SignedLessThan => 4,
IntCC::UnsignedGreaterThanOrEqual => 8 | 2,
IntCC::UnsignedGreaterThan => 2,
IntCC::UnsignedLessThanOrEqual => 8 | 4,
IntCC::UnsignedLessThan => 4,
IntCC::Overflow => 1,
IntCC::NotOverflow => 8 | 4 | 2,
};
Cond { mask }
}
pub fn from_floatcc(cc: FloatCC) -> Cond {
let mask = match cc {
FloatCC::Ordered => 8 | 4 | 2,
FloatCC::Unordered => 1,
FloatCC::Equal => 8,
FloatCC::NotEqual => 4 | 2 | 1,
FloatCC::OrderedNotEqual => 4 | 2,
FloatCC::UnorderedOrEqual => 8 | 1,
FloatCC::LessThan => 4,
FloatCC::LessThanOrEqual => 8 | 4,
FloatCC::GreaterThan => 2,
FloatCC::GreaterThanOrEqual => 8 | 2,
FloatCC::UnorderedOrLessThan => 4 | 1,
FloatCC::UnorderedOrLessThanOrEqual => 8 | 4 | 1,
FloatCC::UnorderedOrGreaterThan => 2 | 1,
FloatCC::UnorderedOrGreaterThanOrEqual => 8 | 2 | 1,
};
Cond { mask }
}
/// Return the inverted condition.
pub fn invert(self) -> Cond {
Cond {
mask: !self.mask & 15,
}
}
/// Return the machine encoding of this condition.
pub fn bits(self) -> u8 {
self.mask
}
}
impl PrettyPrint for MemArg {
fn pretty_print(&self, _: u8, allocs: &mut AllocationConsumer<'_>) -> String {
match self {
&MemArg::BXD12 {
base, index, disp, ..
} => {
let base = allocs.next(base);
let index = allocs.next(index);
if base != zero_reg() {
if index != zero_reg() {
format!(
"{}({},{})",
disp.pretty_print_default(),
show_reg(index),
show_reg(base),
)
} else {
format!("{}({})", disp.pretty_print_default(), show_reg(base))
}
} else {
if index != zero_reg() {
format!("{}({},)", disp.pretty_print_default(), show_reg(index))
} else {
format!("{}", disp.pretty_print_default())
}
}
}
&MemArg::BXD20 {
base, index, disp, ..
} => {
let base = allocs.next(base);
let index = allocs.next(index);
if base != zero_reg() {
if index != zero_reg() {
format!(
"{}({},{})",
disp.pretty_print_default(),
show_reg(index),
show_reg(base),
)
} else {
format!("{}({})", disp.pretty_print_default(), show_reg(base))
}
} else {
if index != zero_reg() {
format!("{}({},)", disp.pretty_print_default(), show_reg(index))
} else {
format!("{}", disp.pretty_print_default())
}
}
}
&MemArg::Label { target } => target.to_string(),
&MemArg::Symbol {
ref name, offset, ..
} => format!("{} + {}", name.display(None), offset),
// Eliminated by `mem_finalize()`.
&MemArg::InitialSPOffset { .. }
| &MemArg::NominalSPOffset { .. }
| &MemArg::RegOffset { .. } => {
panic!("Unexpected pseudo mem-arg mode (stack-offset or generic reg-offset)!")
}
}
}
}
impl PrettyPrint for Cond {
fn pretty_print(&self, _: u8, _: &mut AllocationConsumer<'_>) -> String {
let s = match self.mask {
1 => "o",
2 => "h",
3 => "nle",
4 => "l",
5 => "nhe",
6 => "lh",
7 => "ne",
8 => "e",
9 => "nlh",
10 => "he",
11 => "nl",
12 => "le",
13 => "nh",
14 => "no",
_ => unreachable!(),
};
s.to_string()
}
}