Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/s390x/abi.rs
1693 views
1
//! Implementation of a standard S390x ABI.
2
//!
3
//! This machine uses the "vanilla" ABI implementation from abi.rs,
4
//! however a few details are different from the description there:
5
//!
6
//! - On s390x, the caller must provide a "register save area" of 160
7
//! bytes to any function it calls. The called function is free to use
8
//! this space for any purpose; usually to save callee-saved GPRs.
9
//! (Note that while this area is allocated by the caller, it is counted
10
//! as part of the callee's stack frame; in particular, the callee's CFA
11
//! is the top of the register save area, not the incoming SP value.)
12
//!
13
//! - Overflow arguments are passed on the stack starting immediately
14
//! above the register save area. On s390x, this space is allocated
15
//! only once directly in the prologue, using a size large enough to
16
//! hold overflow arguments for every call in the function.
17
//!
18
//! - On s390x we do not use a frame pointer register; instead, every
19
//! element of the stack frame is addressed via (constant) offsets
20
//! from the stack pointer. Note that due to the above (and because
21
//! there are no variable-sized stack allocations in cranelift), the
22
//! value of the stack pointer register never changes after the
23
//! initial allocation in the function prologue.
24
//!
25
//! - If we are asked to "preserve frame pointers" to enable stack
26
//! unwinding, we use the stack backchain feature instead, which
27
//! is documented by the s390x ELF ABI, but marked as optional.
28
//! This ensures that at all times during execution of a function,
29
//! the lowest word on the stack (part of the register save area)
30
//! holds a copy of the stack pointer at function entry.
31
//!
32
//! Overall, the stack frame layout on s390x is as follows:
33
//!
34
//! ```plain
35
//! (high address)
36
//!
37
//! +---------------------------+
38
//! | ... |
39
//! CFA -----> | stack args |
40
//! +---------------------------+
41
//! | ... |
42
//! | 160 bytes reg save area |
43
//! | (used to save GPRs) |
44
//! SP at function entry -----> | (incl. caller's backchain)|
45
//! +---------------------------+
46
//! | ... |
47
//! | clobbered callee-saves |
48
//! | (used to save FPRs) |
49
//! unwind-frame base ----> | (alloc'd by prologue) |
50
//! +---------------------------+
51
//! | ... |
52
//! | spill slots |
53
//! | (accessed via SP) |
54
//! | ... |
55
//! | stack slots |
56
//! | (accessed via SP) |
57
//! | (alloc'd by prologue) |
58
//! +---------------------------+
59
//! | ... |
60
//! | args for call |
61
//! | outgoing reg save area |
62
//! | (alloc'd by prologue) |
63
//! SP during function ------> | (incl. callee's backchain)|
64
//! +---------------------------+
65
//!
66
//! (low address)
67
//! ```
68
//!
69
//!
70
//! The tail-call ABI has the following changes to the system ABI:
71
//!
72
//! - %r6 and %r7 are both non-callee-saved argument registers.
73
//!
74
//! - The argument save area for outgoing (non-tail) calls to
75
//! a tail-call ABI function is placed *below* the caller's
76
//! stack frame. This means the caller temporarily allocates
77
//! a part of the callee's frame, including temporary space
78
//! for a register save area holding a copy of the backchain.
79
//!
80
//! - For tail calls, the caller puts outgoing arguments at the
81
//! very top of its stack frame, overlapping the incoming
82
//! argument area. This is extended by the prolog if needed.
83
//!
84
//! Overall, the tail-call stack frame layout on s390x is as follows:
85
//!
86
//! ```plain
87
//! (high address)
88
//!
89
//! +---------------------------+
90
//! | ... |
91
//! CFA -----> | (caller's frame) |
92
//! +---------------------------+
93
//! | ... |
94
//! | 160 bytes reg save area |
95
//! | (used to save GPRs) |
96
//! SP at function return-----> | (incl. caller's backchain)|
97
//! +---------------------------+
98
//! | ... |
99
//! | incoming stack args |
100
//! SP at function entry -----> | (incl. backchain copy) |
101
//! +---------------------------+
102
//! | ... |
103
//! | outgoing tail call args |
104
//! | (overlaps incoming args) |
105
//! | (incl. backchain copy) |
106
//! SP at tail cail ----> | (alloc'd by prologue) |
107
//! +---------------------------+
108
//! | ... |
109
//! | clobbered callee-saves |
110
//! | (used to save FPRs) |
111
//! unwind-frame base ----> | (alloc'd by prologue) |
112
//! +---------------------------+
113
//! | ... |
114
//! | spill slots |
115
//! | (accessed via SP) |
116
//! | ... |
117
//! | stack slots |
118
//! | (accessed via SP) |
119
//! | (alloc'd by prologue) |
120
//! +---------------------------+
121
//! | ... |
122
//! | outgoing calls return buf |
123
//! | outgoing reg save area |
124
//! | (alloc'd by prologue) |
125
//! SP during function ------> | (incl. callee's backchain)|
126
//! +---------------------------+
127
//! | ... |
128
//! | outgoing stack args |
129
//! | (alloc'd by call sequence)|
130
//! SP at non-tail call -----> | (incl. backchain copy) |
131
//! +---------------------------+
132
//! (low address)
133
//! ```
134
135
use crate::CodegenResult;
136
use crate::ir;
137
use crate::ir::MemFlags;
138
use crate::ir::Signature;
139
use crate::ir::Type;
140
use crate::ir::condcodes::IntCC;
141
use crate::ir::types;
142
use crate::isa;
143
use crate::isa::s390x::{inst::*, settings as s390x_settings};
144
use crate::isa::unwind::UnwindInst;
145
use crate::machinst::*;
146
use crate::settings;
147
use alloc::vec::Vec;
148
use regalloc2::{MachineEnv, PRegSet};
149
use smallvec::{SmallVec, smallvec};
150
use std::borrow::ToOwned;
151
use std::sync::OnceLock;
152
153
// We use a generic implementation that factors out ABI commonalities.
154
155
/// Support for the S390x ABI from the callee side (within a function body).
156
pub type S390xCallee = Callee<S390xMachineDeps>;
157
158
/// ABI Register usage
159
160
fn in_int_reg(ty: Type) -> bool {
161
match ty {
162
types::I8 | types::I16 | types::I32 | types::I64 => true,
163
_ => false,
164
}
165
}
166
167
fn in_flt_reg(ty: Type) -> bool {
168
match ty {
169
types::F16 | types::F32 | types::F64 => true,
170
_ => false,
171
}
172
}
173
174
fn in_vec_reg(ty: Type) -> bool {
175
ty.is_vector() && ty.bits() == 128
176
}
177
178
fn get_intreg_for_arg(call_conv: isa::CallConv, idx: usize) -> Option<Reg> {
179
match idx {
180
0 => Some(regs::gpr(2)),
181
1 => Some(regs::gpr(3)),
182
2 => Some(regs::gpr(4)),
183
3 => Some(regs::gpr(5)),
184
4 => Some(regs::gpr(6)),
185
5 if call_conv == isa::CallConv::Tail => Some(regs::gpr(7)),
186
_ => None,
187
}
188
}
189
190
fn get_fltreg_for_arg(idx: usize) -> Option<Reg> {
191
match idx {
192
0 => Some(regs::vr(0)),
193
1 => Some(regs::vr(2)),
194
2 => Some(regs::vr(4)),
195
3 => Some(regs::vr(6)),
196
_ => None,
197
}
198
}
199
200
fn get_vecreg_for_arg(idx: usize) -> Option<Reg> {
201
match idx {
202
0 => Some(regs::vr(24)),
203
1 => Some(regs::vr(25)),
204
2 => Some(regs::vr(26)),
205
3 => Some(regs::vr(27)),
206
4 => Some(regs::vr(28)),
207
5 => Some(regs::vr(29)),
208
6 => Some(regs::vr(30)),
209
7 => Some(regs::vr(31)),
210
_ => None,
211
}
212
}
213
214
fn get_intreg_for_ret(call_conv: isa::CallConv, idx: usize) -> Option<Reg> {
215
match idx {
216
0 => Some(regs::gpr(2)),
217
// ABI extension to support multi-value returns:
218
1 => Some(regs::gpr(3)),
219
2 => Some(regs::gpr(4)),
220
3 => Some(regs::gpr(5)),
221
4 if call_conv == isa::CallConv::Tail => Some(regs::gpr(6)),
222
5 if call_conv == isa::CallConv::Tail => Some(regs::gpr(7)),
223
_ => None,
224
}
225
}
226
227
fn get_fltreg_for_ret(idx: usize) -> Option<Reg> {
228
match idx {
229
0 => Some(regs::vr(0)),
230
// ABI extension to support multi-value returns:
231
1 => Some(regs::vr(2)),
232
2 => Some(regs::vr(4)),
233
3 => Some(regs::vr(6)),
234
_ => None,
235
}
236
}
237
238
fn get_vecreg_for_ret(idx: usize) -> Option<Reg> {
239
match idx {
240
0 => Some(regs::vr(24)),
241
// ABI extension to support multi-value returns:
242
1 => Some(regs::vr(25)),
243
2 => Some(regs::vr(26)),
244
3 => Some(regs::vr(27)),
245
4 => Some(regs::vr(28)),
246
5 => Some(regs::vr(29)),
247
6 => Some(regs::vr(30)),
248
7 => Some(regs::vr(31)),
249
_ => None,
250
}
251
}
252
253
/// The size of the register save area
254
pub static REG_SAVE_AREA_SIZE: u32 = 160;
255
256
impl From<StackAMode> for MemArg {
257
fn from(stack: StackAMode) -> MemArg {
258
match stack {
259
StackAMode::IncomingArg(off, stack_args_size) => MemArg::IncomingArgOffset {
260
off: off - stack_args_size as i64,
261
},
262
StackAMode::Slot(off) => MemArg::SlotOffset { off },
263
StackAMode::OutgoingArg(off) => MemArg::OutgoingArgOffset { off },
264
}
265
}
266
}
267
268
/// Lane order to be used for a given calling convention.
269
impl From<isa::CallConv> for LaneOrder {
270
fn from(call_conv: isa::CallConv) -> Self {
271
match call_conv {
272
isa::CallConv::Tail => LaneOrder::LittleEndian,
273
_ => LaneOrder::BigEndian,
274
}
275
}
276
}
277
278
/// S390x-specific ABI behavior. This struct just serves as an implementation
279
/// point for the trait; it is never actually instantiated.
280
pub struct S390xMachineDeps;
281
282
impl IsaFlags for s390x_settings::Flags {}
283
284
impl ABIMachineSpec for S390xMachineDeps {
285
type I = Inst;
286
287
type F = s390x_settings::Flags;
288
289
/// This is the limit for the size of argument and return-value areas on the
290
/// stack. We place a reasonable limit here to avoid integer overflow issues
291
/// with 32-bit arithmetic: for now, 128 MB.
292
const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
293
294
fn word_bits() -> u32 {
295
64
296
}
297
298
/// Return required stack alignment in bytes.
299
fn stack_align(_call_conv: isa::CallConv) -> u32 {
300
8
301
}
302
303
fn compute_arg_locs(
304
call_conv: isa::CallConv,
305
flags: &settings::Flags,
306
params: &[ir::AbiParam],
307
args_or_rets: ArgsOrRets,
308
add_ret_area_ptr: bool,
309
mut args: ArgsAccumulator,
310
) -> CodegenResult<(u32, Option<usize>)> {
311
assert_ne!(
312
call_conv,
313
isa::CallConv::Winch,
314
"s390x does not support the 'winch' calling convention yet"
315
);
316
317
let mut next_gpr = 0;
318
let mut next_fpr = 0;
319
let mut next_vr = 0;
320
let mut next_stack: u32 = 0;
321
322
let ret_area_ptr = if add_ret_area_ptr {
323
debug_assert_eq!(args_or_rets, ArgsOrRets::Args);
324
next_gpr += 1;
325
Some(ABIArg::reg(
326
get_intreg_for_arg(call_conv, 0)
327
.unwrap()
328
.to_real_reg()
329
.unwrap(),
330
types::I64,
331
ir::ArgumentExtension::None,
332
ir::ArgumentPurpose::Normal,
333
))
334
} else {
335
None
336
};
337
338
for mut param in params.into_iter().copied() {
339
if let ir::ArgumentPurpose::StructArgument(_) = param.purpose {
340
panic!(
341
"StructArgument parameters are not supported on s390x. \
342
Use regular pointer arguments instead."
343
);
344
}
345
346
let intreg = in_int_reg(param.value_type);
347
let fltreg = in_flt_reg(param.value_type);
348
let vecreg = in_vec_reg(param.value_type);
349
debug_assert!(intreg as i32 + fltreg as i32 + vecreg as i32 <= 1);
350
351
let (next_reg, candidate, implicit_ref) = if intreg {
352
let candidate = match args_or_rets {
353
ArgsOrRets::Args => get_intreg_for_arg(call_conv, next_gpr),
354
ArgsOrRets::Rets => get_intreg_for_ret(call_conv, next_gpr),
355
};
356
(&mut next_gpr, candidate, None)
357
} else if fltreg {
358
let candidate = match args_or_rets {
359
ArgsOrRets::Args => get_fltreg_for_arg(next_fpr),
360
ArgsOrRets::Rets => get_fltreg_for_ret(next_fpr),
361
};
362
(&mut next_fpr, candidate, None)
363
} else if vecreg {
364
let candidate = match args_or_rets {
365
ArgsOrRets::Args => get_vecreg_for_arg(next_vr),
366
ArgsOrRets::Rets => get_vecreg_for_ret(next_vr),
367
};
368
(&mut next_vr, candidate, None)
369
} else {
370
// We must pass this by implicit reference.
371
if args_or_rets == ArgsOrRets::Rets {
372
// For return values, just force them to memory.
373
(&mut next_gpr, None, None)
374
} else {
375
// For arguments, implicitly convert to pointer type.
376
let implicit_ref = Some(param.value_type);
377
param = ir::AbiParam::new(types::I64);
378
let candidate = get_intreg_for_arg(call_conv, next_gpr);
379
(&mut next_gpr, candidate, implicit_ref)
380
}
381
};
382
383
let slot = if let Some(reg) = candidate {
384
*next_reg += 1;
385
ABIArgSlot::Reg {
386
reg: reg.to_real_reg().unwrap(),
387
ty: param.value_type,
388
extension: param.extension,
389
}
390
} else {
391
if args_or_rets == ArgsOrRets::Rets && !flags.enable_multi_ret_implicit_sret() {
392
return Err(crate::CodegenError::Unsupported(
393
"Too many return values to fit in registers. \
394
Use a StructReturn argument instead. (#9510)"
395
.to_owned(),
396
));
397
}
398
399
// Compute size. Every argument or return value takes a slot of
400
// at least 8 bytes.
401
let size = (ty_bits(param.value_type) / 8) as u32;
402
let slot_size = std::cmp::max(size, 8);
403
404
// Align the stack slot.
405
debug_assert!(slot_size.is_power_of_two());
406
let slot_align = std::cmp::min(slot_size, 8);
407
next_stack = align_to(next_stack, slot_align);
408
409
// If the type is actually of smaller size (and the argument
410
// was not extended), it is passed right-aligned.
411
let offset = if size < slot_size && param.extension == ir::ArgumentExtension::None {
412
slot_size - size
413
} else {
414
0
415
};
416
let offset = (next_stack + offset) as i64;
417
next_stack += slot_size;
418
ABIArgSlot::Stack {
419
offset,
420
ty: param.value_type,
421
extension: param.extension,
422
}
423
};
424
425
if let Some(ty) = implicit_ref {
426
assert!(
427
(ty_bits(ty) / 8) % 8 == 0,
428
"implicit argument size is not properly aligned"
429
);
430
args.push(ABIArg::ImplicitPtrArg {
431
pointer: slot,
432
offset: 0, // Will be filled in later
433
ty,
434
purpose: param.purpose,
435
});
436
} else {
437
args.push(ABIArg::Slots {
438
slots: smallvec![slot],
439
purpose: param.purpose,
440
});
441
}
442
}
443
444
next_stack = align_to(next_stack, 8);
445
446
let extra_arg = if let Some(ret_area_ptr) = ret_area_ptr {
447
args.push_non_formal(ret_area_ptr);
448
Some(args.args().len() - 1)
449
} else {
450
None
451
};
452
453
// After all arguments are in their well-defined location,
454
// allocate buffers for all ImplicitPtrArg arguments.
455
for arg in args.args_mut() {
456
match arg {
457
ABIArg::StructArg { .. } => unreachable!(),
458
ABIArg::ImplicitPtrArg { offset, ty, .. } => {
459
*offset = next_stack as i64;
460
next_stack += (ty_bits(*ty) / 8) as u32;
461
}
462
_ => {}
463
}
464
}
465
466
// With the tail-call convention, arguments are passed in the *callee*'s
467
// frame instead of the caller's frame. This means that the register save
468
// area will lie between the incoming arguments and the return buffer.
469
// Include the size of the register area in the argument area size to
470
// match common code expectation that the return buffer resides immediately
471
// above the argument area.
472
if call_conv == isa::CallConv::Tail && args_or_rets == ArgsOrRets::Args && next_stack != 0 {
473
next_stack += REG_SAVE_AREA_SIZE;
474
}
475
476
Ok((next_stack, extra_arg))
477
}
478
479
fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Inst {
480
Inst::gen_load(into_reg, mem.into(), ty)
481
}
482
483
fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Inst {
484
Inst::gen_store(mem.into(), from_reg, ty)
485
}
486
487
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {
488
Inst::gen_move(to_reg, from_reg, ty)
489
}
490
491
fn gen_extend(
492
to_reg: Writable<Reg>,
493
from_reg: Reg,
494
signed: bool,
495
from_bits: u8,
496
to_bits: u8,
497
) -> Inst {
498
assert!(from_bits < to_bits);
499
Inst::Extend {
500
rd: to_reg,
501
rn: from_reg,
502
signed,
503
from_bits,
504
to_bits,
505
}
506
}
507
508
fn gen_args(args: Vec<ArgPair>) -> Inst {
509
Inst::Args { args }
510
}
511
512
fn gen_rets(rets: Vec<RetPair>) -> Inst {
513
Inst::Rets { rets }
514
}
515
516
fn gen_add_imm(
517
_call_conv: isa::CallConv,
518
into_reg: Writable<Reg>,
519
from_reg: Reg,
520
imm: u32,
521
) -> SmallInstVec<Inst> {
522
let mut insts = SmallVec::new();
523
if let Some(imm) = UImm12::maybe_from_u64(imm as u64) {
524
insts.push(Inst::LoadAddr {
525
rd: into_reg,
526
mem: MemArg::BXD12 {
527
base: from_reg,
528
index: zero_reg(),
529
disp: imm,
530
flags: MemFlags::trusted(),
531
},
532
});
533
} else if let Some(imm) = SImm20::maybe_from_i64(imm as i64) {
534
insts.push(Inst::LoadAddr {
535
rd: into_reg,
536
mem: MemArg::BXD20 {
537
base: from_reg,
538
index: zero_reg(),
539
disp: imm,
540
flags: MemFlags::trusted(),
541
},
542
});
543
} else {
544
if from_reg != into_reg.to_reg() {
545
insts.push(Inst::mov64(into_reg, from_reg));
546
}
547
insts.push(Inst::AluRUImm32 {
548
alu_op: ALUOp::AddLogical64,
549
rd: into_reg,
550
ri: into_reg.to_reg(),
551
imm,
552
});
553
}
554
insts
555
}
556
557
fn gen_stack_lower_bound_trap(limit_reg: Reg) -> SmallInstVec<Inst> {
558
let mut insts = SmallVec::new();
559
insts.push(Inst::CmpTrapRR {
560
op: CmpOp::CmpL64,
561
rn: stack_reg(),
562
rm: limit_reg,
563
cond: Cond::from_intcc(IntCC::UnsignedLessThanOrEqual),
564
trap_code: ir::TrapCode::STACK_OVERFLOW,
565
});
566
insts
567
}
568
569
fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>) -> Inst {
570
let mem = mem.into();
571
Inst::LoadAddr { rd: into_reg, mem }
572
}
573
574
fn get_stacklimit_reg(_call_conv: isa::CallConv) -> Reg {
575
spilltmp_reg()
576
}
577
578
fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
579
let mem = MemArg::reg_plus_off(base, offset.into(), MemFlags::trusted());
580
Inst::gen_load(into_reg, mem, ty)
581
}
582
583
fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
584
let mem = MemArg::reg_plus_off(base, offset.into(), MemFlags::trusted());
585
Inst::gen_store(mem, from_reg, ty)
586
}
587
588
fn gen_sp_reg_adjust(imm: i32) -> SmallInstVec<Inst> {
589
if imm == 0 {
590
return SmallVec::new();
591
}
592
593
let mut insts = SmallVec::new();
594
if let Ok(imm) = i16::try_from(imm) {
595
insts.push(Inst::AluRSImm16 {
596
alu_op: ALUOp::Add64,
597
rd: writable_stack_reg(),
598
ri: stack_reg(),
599
imm,
600
});
601
} else {
602
insts.push(Inst::AluRSImm32 {
603
alu_op: ALUOp::Add64,
604
rd: writable_stack_reg(),
605
ri: stack_reg(),
606
imm,
607
});
608
}
609
insts
610
}
611
612
fn gen_prologue_frame_setup(
613
_call_conv: isa::CallConv,
614
_flags: &settings::Flags,
615
_isa_flags: &s390x_settings::Flags,
616
_frame_layout: &FrameLayout,
617
) -> SmallInstVec<Inst> {
618
SmallVec::new()
619
}
620
621
fn gen_epilogue_frame_restore(
622
_call_conv: isa::CallConv,
623
_flags: &settings::Flags,
624
_isa_flags: &s390x_settings::Flags,
625
_frame_layout: &FrameLayout,
626
) -> SmallInstVec<Inst> {
627
SmallVec::new()
628
}
629
630
fn gen_return(
631
_call_conv: isa::CallConv,
632
_isa_flags: &s390x_settings::Flags,
633
_frame_layout: &FrameLayout,
634
) -> SmallInstVec<Inst> {
635
smallvec![Inst::Ret { link: gpr(14) }]
636
}
637
638
fn gen_probestack(_insts: &mut SmallInstVec<Self::I>, _: u32) {
639
// TODO: implement if we ever require stack probes on an s390x host
640
// (unlikely unless Lucet is ported)
641
unimplemented!("Stack probing is unimplemented on S390x");
642
}
643
644
fn gen_inline_probestack(
645
insts: &mut SmallInstVec<Self::I>,
646
_call_conv: isa::CallConv,
647
frame_size: u32,
648
guard_size: u32,
649
) {
650
// The stack probe loop currently takes 4 instructions and each unrolled
651
// probe takes 2. Set this to 2 to keep the max size to 4 instructions.
652
const PROBE_MAX_UNROLL: u32 = 2;
653
654
// Calculate how many probes we need to perform. Round down, as we only
655
// need to probe whole guard_size regions we'd otherwise skip over.
656
let probe_count = frame_size / guard_size;
657
if probe_count == 0 {
658
// No probe necessary
659
} else if probe_count <= PROBE_MAX_UNROLL {
660
// Unrolled probe loop.
661
for _ in 0..probe_count {
662
insts.extend(Self::gen_sp_reg_adjust(-(guard_size as i32)));
663
664
insts.push(Inst::StoreImm8 {
665
imm: 0,
666
mem: MemArg::reg(stack_reg(), MemFlags::trusted()),
667
});
668
}
669
} else {
670
// Explicit probe loop.
671
672
// Load the number of probes into a register used as loop counter.
673
// `gen_inline_probestack` is called after regalloc2, so we can
674
// use the nonallocatable spilltmp register for this purpose.
675
let probe_count_reg = writable_spilltmp_reg();
676
if let Ok(probe_count) = i16::try_from(probe_count) {
677
insts.push(Inst::Mov32SImm16 {
678
rd: probe_count_reg,
679
imm: probe_count,
680
});
681
} else {
682
insts.push(Inst::Mov32Imm {
683
rd: probe_count_reg,
684
imm: probe_count,
685
});
686
}
687
688
// Emit probe loop. The guard size is assumed to fit in 16 bits.
689
insts.push(Inst::StackProbeLoop {
690
probe_count: probe_count_reg,
691
guard_size: i16::try_from(guard_size).unwrap(),
692
});
693
}
694
695
// Restore the stack pointer to its original position.
696
insts.extend(Self::gen_sp_reg_adjust((probe_count * guard_size) as i32));
697
}
698
699
fn gen_clobber_save(
700
call_conv: isa::CallConv,
701
flags: &settings::Flags,
702
frame_layout: &FrameLayout,
703
) -> SmallVec<[Inst; 16]> {
704
let mut insts = SmallVec::new();
705
706
// With the tail call convention, the caller already allocated the
707
// part of our stack frame that contains incoming arguments.
708
let incoming_tail_args_size = if call_conv == isa::CallConv::Tail {
709
frame_layout.incoming_args_size
710
} else {
711
0
712
};
713
714
// Define unwind stack frame.
715
if flags.unwind_info() {
716
insts.push(Inst::Unwind {
717
inst: UnwindInst::DefineNewFrame {
718
offset_upward_to_caller_sp: REG_SAVE_AREA_SIZE + incoming_tail_args_size,
719
offset_downward_to_clobbers: frame_layout.clobber_size
720
- incoming_tail_args_size,
721
},
722
});
723
}
724
725
// Use STMG to save clobbered GPRs into save area.
726
// Note that we always save SP (%r15) here if anything is saved.
727
if let Some((first_clobbered_gpr, _)) = get_clobbered_gprs(frame_layout) {
728
let mut last_clobbered_gpr = 15;
729
let offset = 8 * first_clobbered_gpr as i64 + incoming_tail_args_size as i64;
730
insts.push(Inst::StoreMultiple64 {
731
rt: gpr(first_clobbered_gpr),
732
rt2: gpr(last_clobbered_gpr),
733
mem: MemArg::reg_plus_off(stack_reg(), offset, MemFlags::trusted()),
734
});
735
if flags.unwind_info() {
736
// Normally, we instruct the unwinder to restore the stack pointer
737
// from its slot in the save area. However, if we have incoming
738
// tail-call arguments, the value saved in that slot is incorrect.
739
// In that case, we instead instruct the unwinder to compute the
740
// unwound SP relative to the current CFA, as CFA == SP + 160.
741
if incoming_tail_args_size != 0 {
742
insts.push(Inst::Unwind {
743
inst: UnwindInst::RegStackOffset {
744
clobber_offset: frame_layout.clobber_size,
745
reg: gpr(last_clobbered_gpr).to_real_reg().unwrap(),
746
},
747
});
748
last_clobbered_gpr = last_clobbered_gpr - 1;
749
}
750
for i in first_clobbered_gpr..(last_clobbered_gpr + 1) {
751
insts.push(Inst::Unwind {
752
inst: UnwindInst::SaveReg {
753
clobber_offset: frame_layout.clobber_size + (i * 8) as u32,
754
reg: gpr(i).to_real_reg().unwrap(),
755
},
756
});
757
}
758
}
759
}
760
761
// Save current stack pointer value if we need to write the backchain.
762
if flags.preserve_frame_pointers() {
763
if incoming_tail_args_size == 0 {
764
insts.push(Inst::mov64(writable_gpr(1), stack_reg()));
765
} else {
766
insts.extend(Self::gen_add_imm(
767
call_conv,
768
writable_gpr(1),
769
stack_reg(),
770
incoming_tail_args_size,
771
));
772
}
773
}
774
775
// Decrement stack pointer.
776
let stack_size = frame_layout.outgoing_args_size as i32
777
+ frame_layout.clobber_size as i32
778
+ frame_layout.fixed_frame_storage_size as i32
779
- incoming_tail_args_size as i32;
780
insts.extend(Self::gen_sp_reg_adjust(-stack_size));
781
if flags.unwind_info() {
782
insts.push(Inst::Unwind {
783
inst: UnwindInst::StackAlloc {
784
size: stack_size as u32,
785
},
786
});
787
}
788
789
// Write the stack backchain if requested, using the value saved above.
790
if flags.preserve_frame_pointers() {
791
insts.push(Inst::Store64 {
792
rd: gpr(1),
793
mem: MemArg::reg_plus_off(stack_reg(), 0, MemFlags::trusted()),
794
});
795
}
796
797
// Save FPRs.
798
for (i, reg) in get_clobbered_fprs(frame_layout).iter().enumerate() {
799
insts.push(Inst::VecStoreLane {
800
size: 64,
801
rd: reg.to_reg().into(),
802
mem: MemArg::reg_plus_off(
803
stack_reg(),
804
(i * 8) as i64
805
+ frame_layout.outgoing_args_size as i64
806
+ frame_layout.fixed_frame_storage_size as i64,
807
MemFlags::trusted(),
808
),
809
lane_imm: 0,
810
});
811
if flags.unwind_info() {
812
insts.push(Inst::Unwind {
813
inst: UnwindInst::SaveReg {
814
clobber_offset: (i * 8) as u32,
815
reg: reg.to_reg(),
816
},
817
});
818
}
819
}
820
821
insts
822
}
823
824
fn gen_clobber_restore(
825
call_conv: isa::CallConv,
826
_flags: &settings::Flags,
827
frame_layout: &FrameLayout,
828
) -> SmallVec<[Inst; 16]> {
829
let mut insts = SmallVec::new();
830
831
// Restore FPRs.
832
insts.extend(gen_restore_fprs(frame_layout));
833
834
// Restore GPRs (including SP).
835
insts.extend(gen_restore_gprs(call_conv, frame_layout, 0));
836
837
insts
838
}
839
840
fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
841
_call_conv: isa::CallConv,
842
_dst: Reg,
843
_src: Reg,
844
_size: usize,
845
_alloc: F,
846
) -> SmallVec<[Self::I; 8]> {
847
unimplemented!("StructArgs not implemented for S390X yet");
848
}
849
850
fn get_number_of_spillslots_for_value(
851
rc: RegClass,
852
_vector_scale: u32,
853
_isa_flags: &Self::F,
854
) -> u32 {
855
// We allocate in terms of 8-byte slots.
856
match rc {
857
RegClass::Int => 1,
858
RegClass::Float => 2,
859
RegClass::Vector => unreachable!(),
860
}
861
}
862
863
fn get_machine_env(_flags: &settings::Flags, call_conv: isa::CallConv) -> &MachineEnv {
864
match call_conv {
865
isa::CallConv::Tail => {
866
static TAIL_MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
867
TAIL_MACHINE_ENV.get_or_init(tail_create_machine_env)
868
}
869
_ => {
870
static SYSV_MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
871
SYSV_MACHINE_ENV.get_or_init(sysv_create_machine_env)
872
}
873
}
874
}
875
876
fn get_regs_clobbered_by_call(
877
call_conv_of_callee: isa::CallConv,
878
is_exception: bool,
879
) -> PRegSet {
880
match call_conv_of_callee {
881
isa::CallConv::Tail if is_exception => ALL_CLOBBERS,
882
isa::CallConv::Tail => TAIL_CLOBBERS,
883
_ => SYSV_CLOBBERS,
884
}
885
}
886
887
fn get_ext_mode(
888
_call_conv: isa::CallConv,
889
specified: ir::ArgumentExtension,
890
) -> ir::ArgumentExtension {
891
specified
892
}
893
894
fn compute_frame_layout(
895
call_conv: isa::CallConv,
896
flags: &settings::Flags,
897
_sig: &Signature,
898
regs: &[Writable<RealReg>],
899
function_calls: FunctionCalls,
900
incoming_args_size: u32,
901
tail_args_size: u32,
902
stackslots_size: u32,
903
fixed_frame_storage_size: u32,
904
mut outgoing_args_size: u32,
905
) -> FrameLayout {
906
assert!(
907
!flags.enable_pinned_reg(),
908
"Pinned register not supported on s390x"
909
);
910
911
let mut regs: Vec<Writable<RealReg>> = regs
912
.iter()
913
.cloned()
914
.filter(|r| is_reg_saved_in_prologue(call_conv, r.to_reg()))
915
.collect();
916
917
// If the front end asks to preserve frame pointers (which we do not
918
// really have in the s390x ABI), we use the stack backchain instead.
919
// For this to work in all cases, we must allocate a stack frame with
920
// at least the outgoing register save area even in leaf functions.
921
// Update our caller's outgoing_args_size to reflect this.
922
if flags.preserve_frame_pointers() {
923
if outgoing_args_size < REG_SAVE_AREA_SIZE {
924
outgoing_args_size = REG_SAVE_AREA_SIZE;
925
}
926
}
927
928
// We need to save/restore the link register in non-leaf functions.
929
// This is not included in the clobber list because we have excluded
930
// call instructions via the is_included_in_clobbers callback.
931
// We also want to enforce saving the link register in leaf functions
932
// for stack unwinding, if we're asked to preserve frame pointers.
933
if outgoing_args_size > 0 {
934
let link_reg = Writable::from_reg(RealReg::from(gpr_preg(14)));
935
if !regs.contains(&link_reg) {
936
regs.push(link_reg);
937
}
938
}
939
940
// Sort registers for deterministic code output. We can do an unstable
941
// sort because the registers will be unique (there are no dups).
942
regs.sort_unstable();
943
944
// Compute clobber size. We only need to count FPR save slots.
945
let mut clobber_size = 0;
946
for reg in &regs {
947
match reg.to_reg().class() {
948
RegClass::Int => {}
949
RegClass::Float => {
950
clobber_size += 8;
951
}
952
RegClass::Vector => unreachable!(),
953
}
954
}
955
956
// Common code assumes that tail-call arguments are part of the caller's
957
// frame. This is not correct for our tail-call convention. To ensure
958
// common code still gets the total size of this stack frame correct,
959
// we add the (incoming and outgoing) taill-call argument size to the
960
// "clobber" size.
961
if call_conv == isa::CallConv::Tail {
962
clobber_size += tail_args_size;
963
}
964
965
// Return FrameLayout structure.
966
FrameLayout {
967
word_bytes: 8,
968
incoming_args_size,
969
// We already accounted for tail-call arguments above, so reset
970
// this value to its default.
971
tail_args_size: incoming_args_size,
972
setup_area_size: 0,
973
clobber_size,
974
fixed_frame_storage_size,
975
stackslots_size,
976
outgoing_args_size,
977
clobbered_callee_saves: regs,
978
function_calls,
979
}
980
}
981
982
fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
983
panic!("Should not be called");
984
}
985
986
fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
987
const PAYLOAD_REGS: &'static [Reg] = &[gpr(6), gpr(7)];
988
match call_conv {
989
isa::CallConv::SystemV | isa::CallConv::Tail => PAYLOAD_REGS,
990
_ => &[],
991
}
992
}
993
}
994
995
impl S390xMachineDeps {
996
pub fn gen_tail_epilogue(
997
frame_layout: &FrameLayout,
998
callee_pop_size: u32,
999
dest: &CallInstDest,
1000
) -> (SmallVec<[Inst; 16]>, Option<Reg>) {
1001
let mut insts = SmallVec::new();
1002
let call_conv = isa::CallConv::Tail;
1003
1004
// Restore FPRs.
1005
insts.extend(gen_restore_fprs(frame_layout));
1006
1007
// If the tail call target is in a callee-saved GPR, we need to move it
1008
// to %r1 (as the only available temp register) before restoring GPRs
1009
// (but after restoring FPRs, which might clobber %r1).
1010
let temp_dest = match dest {
1011
CallInstDest::Indirect { reg }
1012
if reg.to_real_reg().is_some()
1013
&& is_reg_saved_in_prologue(call_conv, reg.to_real_reg().unwrap()) =>
1014
{
1015
insts.push(Inst::Mov64 {
1016
rd: writable_gpr(1),
1017
rm: *reg,
1018
});
1019
Some(gpr(1))
1020
}
1021
_ => None,
1022
};
1023
1024
// Restore GPRs (including SP).
1025
insts.extend(gen_restore_gprs(call_conv, frame_layout, callee_pop_size));
1026
1027
(insts, temp_dest)
1028
}
1029
1030
/// Emit loads for any stack-carried return values using the call
1031
/// info and allocations. In addition, emit lane swaps for all
1032
/// vector-types return values if needed.
1033
pub fn gen_retval_loads(info: &CallInfo<CallInstDest>) -> SmallInstVec<Inst> {
1034
let mut insts = SmallVec::new();
1035
1036
// Helper routine to lane-swap a register if needed.
1037
let lane_swap_if_needed = |insts: &mut SmallInstVec<Inst>, vreg, ty: Type| {
1038
if LaneOrder::from(info.caller_conv) != LaneOrder::from(info.callee_conv) {
1039
if ty.is_vector() && ty.lane_count() >= 2 {
1040
insts.push(Inst::VecEltRev {
1041
lane_count: ty.lane_count(),
1042
rd: vreg,
1043
rn: vreg.to_reg(),
1044
});
1045
}
1046
}
1047
};
1048
1049
// Helper routine to allocate a temp register for ty.
1050
let temp_reg = |ty| match Inst::rc_for_type(ty).unwrap() {
1051
(&[RegClass::Int], _) => writable_gpr(0),
1052
(&[RegClass::Float], _) => writable_vr(1),
1053
_ => unreachable!(),
1054
};
1055
1056
// Do a first pass over the return locations to handle copies that
1057
// need temp registers. These need to be done before regular stack
1058
// loads in case the destination of a load happens to be our temp
1059
// register. (The temp registers by choice are distinct from all
1060
// real return registers, which we verify here again.)
1061
for CallRetPair { vreg, location } in &info.defs {
1062
match location {
1063
RetLocation::Reg(preg, ty) => {
1064
debug_assert!(*preg != temp_reg(*ty).to_reg());
1065
}
1066
RetLocation::Stack(amode, ty) => {
1067
if let Some(spillslot) = vreg.to_reg().to_spillslot() {
1068
let temp = temp_reg(*ty);
1069
insts.push(Inst::gen_load(temp, (*amode).into(), *ty));
1070
lane_swap_if_needed(&mut insts, temp, *ty);
1071
insts.push(Inst::gen_store(
1072
MemArg::SpillOffset {
1073
off: 8 * (spillslot.index() as i64),
1074
},
1075
temp.to_reg(),
1076
Inst::canonical_type_for_rc(temp.to_reg().class()),
1077
));
1078
}
1079
}
1080
}
1081
}
1082
// Now handle all remaining return locations.
1083
for CallRetPair { vreg, location } in &info.defs {
1084
match location {
1085
RetLocation::Reg(preg, ty) => {
1086
lane_swap_if_needed(&mut insts, Writable::from_reg(*preg), *ty);
1087
}
1088
RetLocation::Stack(amode, ty) => {
1089
if vreg.to_reg().to_spillslot().is_none() {
1090
insts.push(Inst::gen_load(*vreg, (*amode).into(), *ty));
1091
lane_swap_if_needed(&mut insts, *vreg, *ty);
1092
}
1093
}
1094
}
1095
}
1096
insts
1097
}
1098
}
1099
1100
fn is_reg_saved_in_prologue(call_conv: isa::CallConv, r: RealReg) -> bool {
1101
match (call_conv, r.class()) {
1102
(isa::CallConv::Tail, RegClass::Int) => {
1103
// r8 - r15 inclusive are callee-saves.
1104
r.hw_enc() >= 8 && r.hw_enc() <= 15
1105
}
1106
(_, RegClass::Int) => {
1107
// r6 - r15 inclusive are callee-saves.
1108
r.hw_enc() >= 6 && r.hw_enc() <= 15
1109
}
1110
(_, RegClass::Float) => {
1111
// f8 - f15 inclusive are callee-saves.
1112
r.hw_enc() >= 8 && r.hw_enc() <= 15
1113
}
1114
(_, RegClass::Vector) => unreachable!(),
1115
}
1116
}
1117
1118
fn get_clobbered_gprs(frame_layout: &FrameLayout) -> Option<(u8, u8)> {
1119
// Collect clobbered GPRs. Note we save/restore GPR always as
1120
// a block of registers using LOAD MULTIPLE / STORE MULTIPLE, starting
1121
// with the clobbered GPR with the lowest number up to the clobbered GPR
1122
// with the highest number.
1123
let (clobbered_gpr, _) = frame_layout.clobbered_callee_saves_by_class();
1124
if clobbered_gpr.is_empty() {
1125
return None;
1126
}
1127
1128
let first = clobbered_gpr.first().unwrap().to_reg().hw_enc();
1129
let last = clobbered_gpr.last().unwrap().to_reg().hw_enc();
1130
debug_assert!(clobbered_gpr.iter().all(|r| r.to_reg().hw_enc() >= first));
1131
debug_assert!(clobbered_gpr.iter().all(|r| r.to_reg().hw_enc() <= last));
1132
Some((first, last))
1133
}
1134
1135
fn get_clobbered_fprs(frame_layout: &FrameLayout) -> &[Writable<RealReg>] {
1136
// Collect clobbered floating-point registers.
1137
let (_, clobbered_fpr) = frame_layout.clobbered_callee_saves_by_class();
1138
clobbered_fpr
1139
}
1140
1141
// Restore GPRs (including SP) from the register save area.
1142
// This must not clobber any register, specifically including %r1.
1143
fn gen_restore_gprs(
1144
call_conv: isa::CallConv,
1145
frame_layout: &FrameLayout,
1146
callee_pop_size: u32,
1147
) -> SmallVec<[Inst; 16]> {
1148
let mut insts = SmallVec::new();
1149
1150
// Determine GPRs to be restored.
1151
let clobbered_gpr = get_clobbered_gprs(frame_layout);
1152
1153
// Increment stack pointer unless it will be restored implicitly.
1154
// Note that implicit stack pointer restoration cannot be done in the
1155
// presence of either incoming or outgoing tail call arguments.
1156
let stack_size = frame_layout.outgoing_args_size as i32
1157
+ frame_layout.clobber_size as i32
1158
+ frame_layout.fixed_frame_storage_size as i32;
1159
let implicit_sp_restore = callee_pop_size == 0
1160
&& (call_conv != isa::CallConv::Tail || frame_layout.incoming_args_size == 0)
1161
&& clobbered_gpr.map_or(false, |(first, _)| {
1162
SImm20::maybe_from_i64(8 * first as i64 + stack_size as i64).is_some()
1163
});
1164
if !implicit_sp_restore {
1165
insts.extend(S390xMachineDeps::gen_sp_reg_adjust(
1166
stack_size - callee_pop_size as i32,
1167
));
1168
}
1169
1170
// Use LMG to restore clobbered GPRs from save area.
1171
if let Some((first, mut last)) = clobbered_gpr {
1172
// Attempt to restore via SP, taking implicit restoration into account.
1173
let mut reg = stack_reg();
1174
let mut offset = callee_pop_size as i64 + 8 * first as i64;
1175
if implicit_sp_restore {
1176
offset += stack_size as i64 - callee_pop_size as i64;
1177
last = 15;
1178
}
1179
// If the offset still overflows, use the first restored GPR
1180
// as temporary holding the address, as we cannot use %r1.
1181
if SImm20::maybe_from_i64(offset).is_none() {
1182
insts.extend(S390xMachineDeps::gen_add_imm(
1183
call_conv,
1184
writable_gpr(first),
1185
stack_reg(),
1186
offset as u32,
1187
));
1188
reg = gpr(first);
1189
offset = 0;
1190
}
1191
// Now this LMG will always have an in-range offset.
1192
insts.push(Inst::LoadMultiple64 {
1193
rt: writable_gpr(first),
1194
rt2: writable_gpr(last),
1195
mem: MemArg::reg_plus_off(reg, offset, MemFlags::trusted()),
1196
});
1197
}
1198
1199
insts
1200
}
1201
1202
// Restore FPRs from the clobber area.
1203
fn gen_restore_fprs(frame_layout: &FrameLayout) -> SmallVec<[Inst; 16]> {
1204
let mut insts = SmallVec::new();
1205
1206
// Determine FPRs to be restored.
1207
let clobbered_fpr = get_clobbered_fprs(frame_layout);
1208
1209
// Restore FPRs.
1210
for (i, reg) in clobbered_fpr.iter().enumerate() {
1211
insts.push(Inst::VecLoadLaneUndef {
1212
size: 64,
1213
rd: Writable::from_reg(reg.to_reg().into()),
1214
mem: MemArg::reg_plus_off(
1215
stack_reg(),
1216
(i * 8) as i64
1217
+ frame_layout.outgoing_args_size as i64
1218
+ frame_layout.fixed_frame_storage_size as i64,
1219
MemFlags::trusted(),
1220
),
1221
lane_imm: 0,
1222
});
1223
}
1224
1225
insts
1226
}
1227
1228
const fn sysv_clobbers() -> PRegSet {
1229
PRegSet::empty()
1230
.with(gpr_preg(0))
1231
.with(gpr_preg(1))
1232
.with(gpr_preg(2))
1233
.with(gpr_preg(3))
1234
.with(gpr_preg(4))
1235
.with(gpr_preg(5))
1236
// v0 - v7 inclusive and v16 - v31 inclusive are
1237
// caller-saves. The upper 64 bits of v8 - v15 inclusive are
1238
// also caller-saves. However, because we cannot currently
1239
// represent partial registers to regalloc2, we indicate here
1240
// that every vector register is caller-save. Because this
1241
// function is used at *callsites*, approximating in this
1242
// direction (save more than necessary) is conservative and
1243
// thus safe.
1244
//
1245
// Note that we exclude clobbers from a call instruction when
1246
// a call instruction's callee has the same ABI as the caller
1247
// (the current function body); this is safe (anything
1248
// clobbered by callee can be clobbered by caller as well) and
1249
// avoids unnecessary saves of v8-v15 in the prologue even
1250
// though we include them as defs here.
1251
.with(vr_preg(0))
1252
.with(vr_preg(1))
1253
.with(vr_preg(2))
1254
.with(vr_preg(3))
1255
.with(vr_preg(4))
1256
.with(vr_preg(5))
1257
.with(vr_preg(6))
1258
.with(vr_preg(7))
1259
.with(vr_preg(8))
1260
.with(vr_preg(9))
1261
.with(vr_preg(10))
1262
.with(vr_preg(11))
1263
.with(vr_preg(12))
1264
.with(vr_preg(13))
1265
.with(vr_preg(14))
1266
.with(vr_preg(15))
1267
.with(vr_preg(16))
1268
.with(vr_preg(17))
1269
.with(vr_preg(18))
1270
.with(vr_preg(19))
1271
.with(vr_preg(20))
1272
.with(vr_preg(21))
1273
.with(vr_preg(22))
1274
.with(vr_preg(23))
1275
.with(vr_preg(24))
1276
.with(vr_preg(25))
1277
.with(vr_preg(26))
1278
.with(vr_preg(27))
1279
.with(vr_preg(28))
1280
.with(vr_preg(29))
1281
.with(vr_preg(30))
1282
.with(vr_preg(31))
1283
}
1284
const SYSV_CLOBBERS: PRegSet = sysv_clobbers();
1285
1286
const fn tail_clobbers() -> PRegSet {
1287
// Same as the SystemV ABI, except that %r6 and %r7 are clobbered.
1288
PRegSet::empty()
1289
.with(gpr_preg(0))
1290
.with(gpr_preg(1))
1291
.with(gpr_preg(2))
1292
.with(gpr_preg(3))
1293
.with(gpr_preg(4))
1294
.with(gpr_preg(5))
1295
.with(gpr_preg(6))
1296
.with(gpr_preg(7))
1297
.with(vr_preg(0))
1298
.with(vr_preg(1))
1299
.with(vr_preg(2))
1300
.with(vr_preg(3))
1301
.with(vr_preg(4))
1302
.with(vr_preg(5))
1303
.with(vr_preg(6))
1304
.with(vr_preg(7))
1305
.with(vr_preg(8))
1306
.with(vr_preg(9))
1307
.with(vr_preg(10))
1308
.with(vr_preg(11))
1309
.with(vr_preg(12))
1310
.with(vr_preg(13))
1311
.with(vr_preg(14))
1312
.with(vr_preg(15))
1313
.with(vr_preg(16))
1314
.with(vr_preg(17))
1315
.with(vr_preg(18))
1316
.with(vr_preg(19))
1317
.with(vr_preg(20))
1318
.with(vr_preg(21))
1319
.with(vr_preg(22))
1320
.with(vr_preg(23))
1321
.with(vr_preg(24))
1322
.with(vr_preg(25))
1323
.with(vr_preg(26))
1324
.with(vr_preg(27))
1325
.with(vr_preg(28))
1326
.with(vr_preg(29))
1327
.with(vr_preg(30))
1328
.with(vr_preg(31))
1329
}
1330
const TAIL_CLOBBERS: PRegSet = tail_clobbers();
1331
1332
const fn all_clobbers() -> PRegSet {
1333
PRegSet::empty()
1334
.with(gpr_preg(0))
1335
.with(gpr_preg(1))
1336
.with(gpr_preg(2))
1337
.with(gpr_preg(3))
1338
.with(gpr_preg(4))
1339
.with(gpr_preg(5))
1340
.with(gpr_preg(6))
1341
.with(gpr_preg(7))
1342
.with(gpr_preg(8))
1343
.with(gpr_preg(9))
1344
.with(gpr_preg(10))
1345
.with(gpr_preg(11))
1346
.with(gpr_preg(12))
1347
.with(gpr_preg(13))
1348
.with(gpr_preg(14))
1349
.with(gpr_preg(15))
1350
.with(vr_preg(0))
1351
.with(vr_preg(1))
1352
.with(vr_preg(2))
1353
.with(vr_preg(3))
1354
.with(vr_preg(4))
1355
.with(vr_preg(5))
1356
.with(vr_preg(6))
1357
.with(vr_preg(7))
1358
.with(vr_preg(8))
1359
.with(vr_preg(9))
1360
.with(vr_preg(10))
1361
.with(vr_preg(11))
1362
.with(vr_preg(12))
1363
.with(vr_preg(13))
1364
.with(vr_preg(14))
1365
.with(vr_preg(15))
1366
.with(vr_preg(16))
1367
.with(vr_preg(17))
1368
.with(vr_preg(18))
1369
.with(vr_preg(19))
1370
.with(vr_preg(20))
1371
.with(vr_preg(21))
1372
.with(vr_preg(22))
1373
.with(vr_preg(23))
1374
.with(vr_preg(24))
1375
.with(vr_preg(25))
1376
.with(vr_preg(26))
1377
.with(vr_preg(27))
1378
.with(vr_preg(28))
1379
.with(vr_preg(29))
1380
.with(vr_preg(30))
1381
.with(vr_preg(31))
1382
}
1383
const ALL_CLOBBERS: PRegSet = all_clobbers();
1384
1385
fn sysv_create_machine_env() -> MachineEnv {
1386
MachineEnv {
1387
preferred_regs_by_class: [
1388
vec![
1389
// no r0; can't use for addressing?
1390
// no r1; it is our spilltmp.
1391
gpr_preg(2),
1392
gpr_preg(3),
1393
gpr_preg(4),
1394
gpr_preg(5),
1395
],
1396
vec![
1397
vr_preg(0),
1398
vr_preg(1),
1399
vr_preg(2),
1400
vr_preg(3),
1401
vr_preg(4),
1402
vr_preg(5),
1403
vr_preg(6),
1404
vr_preg(7),
1405
vr_preg(16),
1406
vr_preg(17),
1407
vr_preg(18),
1408
vr_preg(19),
1409
vr_preg(20),
1410
vr_preg(21),
1411
vr_preg(22),
1412
vr_preg(23),
1413
vr_preg(24),
1414
vr_preg(25),
1415
vr_preg(26),
1416
vr_preg(27),
1417
vr_preg(28),
1418
vr_preg(29),
1419
vr_preg(30),
1420
vr_preg(31),
1421
],
1422
// Vector Regclass is unused
1423
vec![],
1424
],
1425
non_preferred_regs_by_class: [
1426
vec![
1427
gpr_preg(6),
1428
gpr_preg(7),
1429
gpr_preg(8),
1430
gpr_preg(9),
1431
gpr_preg(10),
1432
gpr_preg(11),
1433
gpr_preg(12),
1434
gpr_preg(13),
1435
gpr_preg(14),
1436
// no r15; it is the stack pointer.
1437
],
1438
vec![
1439
vr_preg(8),
1440
vr_preg(9),
1441
vr_preg(10),
1442
vr_preg(11),
1443
vr_preg(12),
1444
vr_preg(13),
1445
vr_preg(14),
1446
vr_preg(15),
1447
],
1448
// Vector Regclass is unused
1449
vec![],
1450
],
1451
fixed_stack_slots: vec![],
1452
scratch_by_class: [None, None, None],
1453
}
1454
}
1455
1456
fn tail_create_machine_env() -> MachineEnv {
1457
// Same as the SystemV ABI, except that %r6 and %r7 are preferred.
1458
MachineEnv {
1459
preferred_regs_by_class: [
1460
vec![
1461
// no r0; can't use for addressing?
1462
// no r1; it is our spilltmp.
1463
gpr_preg(2),
1464
gpr_preg(3),
1465
gpr_preg(4),
1466
gpr_preg(5),
1467
gpr_preg(6),
1468
gpr_preg(7),
1469
],
1470
vec![
1471
vr_preg(0),
1472
vr_preg(1),
1473
vr_preg(2),
1474
vr_preg(3),
1475
vr_preg(4),
1476
vr_preg(5),
1477
vr_preg(6),
1478
vr_preg(7),
1479
vr_preg(16),
1480
vr_preg(17),
1481
vr_preg(18),
1482
vr_preg(19),
1483
vr_preg(20),
1484
vr_preg(21),
1485
vr_preg(22),
1486
vr_preg(23),
1487
vr_preg(24),
1488
vr_preg(25),
1489
vr_preg(26),
1490
vr_preg(27),
1491
vr_preg(28),
1492
vr_preg(29),
1493
vr_preg(30),
1494
vr_preg(31),
1495
],
1496
// Vector Regclass is unused
1497
vec![],
1498
],
1499
non_preferred_regs_by_class: [
1500
vec![
1501
gpr_preg(8),
1502
gpr_preg(9),
1503
gpr_preg(10),
1504
gpr_preg(11),
1505
gpr_preg(12),
1506
gpr_preg(13),
1507
gpr_preg(14),
1508
// no r15; it is the stack pointer.
1509
],
1510
vec![
1511
vr_preg(8),
1512
vr_preg(9),
1513
vr_preg(10),
1514
vr_preg(11),
1515
vr_preg(12),
1516
vr_preg(13),
1517
vr_preg(14),
1518
vr_preg(15),
1519
],
1520
// Vector Regclass is unused
1521
vec![],
1522
],
1523
fixed_stack_slots: vec![],
1524
scratch_by_class: [None, None, None],
1525
}
1526
}
1527
1528