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