1 //! S390x ISA definitions: instruction arguments. 2 3 // Some variants are never constructed, but we still want them as options in the future. 4 #![allow(dead_code)] 5 6 use crate::ir::condcodes::{FloatCC, IntCC}; 7 use crate::ir::MemFlags; 8 use crate::isa::s390x::inst::*; 9 use crate::machinst::MachLabel; 10 11 use regalloc::{PrettyPrint, RealRegUniverse, Reg}; 12 13 use std::string::String; 14 15 //============================================================================= 16 // Instruction sub-components (memory addresses): definitions 17 18 /// A memory argument to load/store, encapsulating the possible addressing modes. 19 #[derive(Clone, Debug)] 20 pub enum MemArg { 21 // 22 // Real IBM Z addressing modes: 23 // 24 /// Base register, index register, and 12-bit unsigned displacement. 25 BXD12 { 26 base: Reg, 27 index: Reg, 28 disp: UImm12, 29 flags: MemFlags, 30 }, 31 32 /// Base register, index register, and 20-bit signed displacement. 33 BXD20 { 34 base: Reg, 35 index: Reg, 36 disp: SImm20, 37 flags: MemFlags, 38 }, 39 40 /// PC-relative Reference to a label. 41 Label { target: BranchTarget }, 42 43 /// PC-relative Reference to a near symbol. 44 Symbol { 45 name: Box<ExternalName>, 46 offset: i32, 47 flags: MemFlags, 48 }, 49 50 // 51 // Virtual addressing modes that are lowered at emission time: 52 // 53 /// Arbitrary offset from a register. Converted to generation of large 54 /// offsets with multiple instructions as necessary during code emission. 55 RegOffset { reg: Reg, off: i64, flags: MemFlags }, 56 57 /// Offset from the stack pointer at function entry. 58 InitialSPOffset { off: i64 }, 59 60 /// Offset from the "nominal stack pointer", which is where the real SP is 61 /// just after stack and spill slots are allocated in the function prologue. 62 /// At emission time, this is converted to `SPOffset` with a fixup added to 63 /// the offset constant. The fixup is a running value that is tracked as 64 /// emission iterates through instructions in linear order, and can be 65 /// adjusted up and down with [Inst::VirtualSPOffsetAdj]. 66 /// 67 /// The standard ABI is in charge of handling this (by emitting the 68 /// adjustment meta-instructions). It maintains the invariant that "nominal 69 /// SP" is where the actual SP is after the function prologue and before 70 /// clobber pushes. See the diagram in the documentation for 71 /// [crate::isa::s390x::abi](the ABI module) for more details. 72 NominalSPOffset { off: i64 }, 73 } 74 75 impl MemArg { 76 /// Memory reference using an address in a register. reg(reg: Reg, flags: MemFlags) -> MemArg77 pub fn reg(reg: Reg, flags: MemFlags) -> MemArg { 78 MemArg::BXD12 { 79 base: reg, 80 index: zero_reg(), 81 disp: UImm12::zero(), 82 flags, 83 } 84 } 85 86 /// Memory reference using the sum of two registers as an address. reg_plus_reg(reg1: Reg, reg2: Reg, flags: MemFlags) -> MemArg87 pub fn reg_plus_reg(reg1: Reg, reg2: Reg, flags: MemFlags) -> MemArg { 88 MemArg::BXD12 { 89 base: reg1, 90 index: reg2, 91 disp: UImm12::zero(), 92 flags, 93 } 94 } 95 96 /// Memory reference using the sum of a register an an offset as address. reg_plus_off(reg: Reg, off: i64, flags: MemFlags) -> MemArg97 pub fn reg_plus_off(reg: Reg, off: i64, flags: MemFlags) -> MemArg { 98 MemArg::RegOffset { reg, off, flags } 99 } 100 get_flags(&self) -> MemFlags101 pub(crate) fn get_flags(&self) -> MemFlags { 102 match self { 103 MemArg::BXD12 { flags, .. } => *flags, 104 MemArg::BXD20 { flags, .. } => *flags, 105 MemArg::RegOffset { flags, .. } => *flags, 106 MemArg::Label { .. } => MemFlags::trusted(), 107 MemArg::Symbol { flags, .. } => *flags, 108 MemArg::InitialSPOffset { .. } => MemFlags::trusted(), 109 MemArg::NominalSPOffset { .. } => MemFlags::trusted(), 110 } 111 } 112 can_trap(&self) -> bool113 pub(crate) fn can_trap(&self) -> bool { 114 !self.get_flags().notrap() 115 } 116 } 117 118 //============================================================================= 119 // Instruction sub-components (conditions, branches and branch targets): 120 // definitions 121 122 /// Condition for conditional branches. 123 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 124 pub struct Cond { 125 mask: u8, 126 } 127 128 impl Cond { from_mask(mask: u8) -> Cond129 pub fn from_mask(mask: u8) -> Cond { 130 assert!(mask >= 1 && mask <= 14); 131 Cond { mask } 132 } 133 from_intcc(cc: IntCC) -> Cond134 pub fn from_intcc(cc: IntCC) -> Cond { 135 let mask = match cc { 136 IntCC::Equal => 8, 137 IntCC::NotEqual => 4 | 2, 138 IntCC::SignedGreaterThanOrEqual => 8 | 2, 139 IntCC::SignedGreaterThan => 2, 140 IntCC::SignedLessThanOrEqual => 8 | 4, 141 IntCC::SignedLessThan => 4, 142 IntCC::UnsignedGreaterThanOrEqual => 8 | 2, 143 IntCC::UnsignedGreaterThan => 2, 144 IntCC::UnsignedLessThanOrEqual => 8 | 4, 145 IntCC::UnsignedLessThan => 4, 146 IntCC::Overflow => 1, 147 IntCC::NotOverflow => 8 | 4 | 2, 148 }; 149 Cond { mask } 150 } 151 from_floatcc(cc: FloatCC) -> Cond152 pub fn from_floatcc(cc: FloatCC) -> Cond { 153 let mask = match cc { 154 FloatCC::Ordered => 8 | 4 | 2, 155 FloatCC::Unordered => 1, 156 FloatCC::Equal => 8, 157 FloatCC::NotEqual => 4 | 2 | 1, 158 FloatCC::OrderedNotEqual => 4 | 2, 159 FloatCC::UnorderedOrEqual => 8 | 1, 160 FloatCC::LessThan => 4, 161 FloatCC::LessThanOrEqual => 8 | 4, 162 FloatCC::GreaterThan => 2, 163 FloatCC::GreaterThanOrEqual => 8 | 2, 164 FloatCC::UnorderedOrLessThan => 4 | 1, 165 FloatCC::UnorderedOrLessThanOrEqual => 8 | 4 | 1, 166 FloatCC::UnorderedOrGreaterThan => 2 | 1, 167 FloatCC::UnorderedOrGreaterThanOrEqual => 8 | 2 | 1, 168 }; 169 Cond { mask } 170 } 171 172 /// Return the inverted condition. invert(self) -> Cond173 pub fn invert(self) -> Cond { 174 Cond { 175 mask: !self.mask & 15, 176 } 177 } 178 179 /// Return the machine encoding of this condition. bits(self) -> u8180 pub fn bits(self) -> u8 { 181 self.mask 182 } 183 } 184 185 /// A branch target. Either unresolved (basic-block index) or resolved (offset 186 /// from end of current instruction). 187 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 188 pub enum BranchTarget { 189 /// An unresolved reference to a Label, as passed into 190 /// `lower_branch_group()`. 191 Label(MachLabel), 192 /// A fixed PC offset. 193 ResolvedOffset(i32), 194 } 195 196 impl BranchTarget { 197 /// Return the target's label, if it is a label-based target. as_label(self) -> Option<MachLabel>198 pub fn as_label(self) -> Option<MachLabel> { 199 match self { 200 BranchTarget::Label(l) => Some(l), 201 _ => None, 202 } 203 } 204 205 /// Return the target's offset, if specified, or zero if label-based. as_ri_offset_or_zero(self) -> u16206 pub fn as_ri_offset_or_zero(self) -> u16 { 207 let off = match self { 208 BranchTarget::ResolvedOffset(off) => off >> 1, 209 _ => 0, 210 }; 211 assert!(off <= 0x7fff); 212 assert!(off >= -0x8000); 213 off as u16 214 } 215 216 /// Return the target's offset, if specified, or zero if label-based. as_ril_offset_or_zero(self) -> u32217 pub fn as_ril_offset_or_zero(self) -> u32 { 218 let off = match self { 219 BranchTarget::ResolvedOffset(off) => off >> 1, 220 _ => 0, 221 }; 222 off as u32 223 } 224 } 225 226 impl PrettyPrint for MemArg { show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String227 fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String { 228 match self { 229 &MemArg::BXD12 { 230 base, index, disp, .. 231 } => { 232 if base != zero_reg() { 233 if index != zero_reg() { 234 format!( 235 "{}({},{})", 236 disp.show_rru(mb_rru), 237 index.show_rru(mb_rru), 238 base.show_rru(mb_rru) 239 ) 240 } else { 241 format!("{}({})", disp.show_rru(mb_rru), base.show_rru(mb_rru)) 242 } 243 } else { 244 if index != zero_reg() { 245 format!("{}({},)", disp.show_rru(mb_rru), index.show_rru(mb_rru)) 246 } else { 247 format!("{}", disp.show_rru(mb_rru)) 248 } 249 } 250 } 251 &MemArg::BXD20 { 252 base, index, disp, .. 253 } => { 254 if base != zero_reg() { 255 if index != zero_reg() { 256 format!( 257 "{}({},{})", 258 disp.show_rru(mb_rru), 259 index.show_rru(mb_rru), 260 base.show_rru(mb_rru) 261 ) 262 } else { 263 format!("{}({})", disp.show_rru(mb_rru), base.show_rru(mb_rru)) 264 } 265 } else { 266 if index != zero_reg() { 267 format!("{}({},)", disp.show_rru(mb_rru), index.show_rru(mb_rru)) 268 } else { 269 format!("{}", disp.show_rru(mb_rru)) 270 } 271 } 272 } 273 &MemArg::Label { ref target } => target.show_rru(mb_rru), 274 &MemArg::Symbol { 275 ref name, offset, .. 276 } => format!("{} + {}", name, offset), 277 // Eliminated by `mem_finalize()`. 278 &MemArg::InitialSPOffset { .. } 279 | &MemArg::NominalSPOffset { .. } 280 | &MemArg::RegOffset { .. } => { 281 panic!("Unexpected pseudo mem-arg mode (stack-offset or generic reg-offset)!") 282 } 283 } 284 } 285 } 286 287 impl PrettyPrint for Cond { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String288 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 289 let s = match self.mask { 290 1 => "o", 291 2 => "h", 292 3 => "nle", 293 4 => "l", 294 5 => "nhe", 295 6 => "lh", 296 7 => "ne", 297 8 => "e", 298 9 => "nlh", 299 10 => "he", 300 11 => "nl", 301 12 => "le", 302 13 => "nh", 303 14 => "no", 304 _ => unreachable!(), 305 }; 306 s.to_string() 307 } 308 } 309 310 impl PrettyPrint for BranchTarget { show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String311 fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { 312 match self { 313 &BranchTarget::Label(label) => format!("label{:?}", label.get()), 314 &BranchTarget::ResolvedOffset(off) => format!("{}", off), 315 } 316 } 317 } 318