Browse Source

Support IBM z/Architecture

This adds support for the IBM z/Architecture (s390x-ibm-linux).

The status of the s390x backend in its current form is:
- Wasmtime is fully functional and passes all tests on s390x.
- All back-end features supported, with the exception of SIMD.
- There is still a lot of potential for performance improvements.
- Currently the only supported processor type is z15.
pull/2874/head
Ulrich Weigand 4 years ago
parent
commit
89b5fc776d
  1. 7
      build.rs
  2. 4
      cranelift/codegen/Cargo.toml
  3. 7
      cranelift/codegen/meta/src/isa/mod.rs
  4. 31
      cranelift/codegen/meta/src/isa/s390x/mod.rs
  5. 3
      cranelift/codegen/meta/src/lib.rs
  6. 4
      cranelift/codegen/src/isa/mod.rs
  7. 770
      cranelift/codegen/src/isa/s390x/abi.rs
  8. 317
      cranelift/codegen/src/isa/s390x/inst/args.rs
  9. 1965
      cranelift/codegen/src/isa/s390x/inst/emit.rs
  10. 7140
      cranelift/codegen/src/isa/s390x/inst/emit_tests.rs
  11. 231
      cranelift/codegen/src/isa/s390x/inst/imms.rs
  12. 3411
      cranelift/codegen/src/isa/s390x/inst/mod.rs
  13. 168
      cranelift/codegen/src/isa/s390x/inst/regs.rs
  14. 2
      cranelift/codegen/src/isa/s390x/inst/unwind.rs
  15. 197
      cranelift/codegen/src/isa/s390x/inst/unwind/systemv.rs
  16. 2839
      cranelift/codegen/src/isa/s390x/lower.rs
  17. 296
      cranelift/codegen/src/isa/s390x/mod.rs
  18. 9
      cranelift/codegen/src/isa/s390x/settings.rs
  19. 1136
      cranelift/filetests/filetests/isa/s390x/arithmetic.clif
  20. 243
      cranelift/filetests/filetests/isa/s390x/bitops.clif
  21. 490
      cranelift/filetests/filetests/isa/s390x/bitwise.clif
  22. 113
      cranelift/filetests/filetests/isa/s390x/call.clif
  23. 62
      cranelift/filetests/filetests/isa/s390x/condbr.clif
  24. 43
      cranelift/filetests/filetests/isa/s390x/condops.clif
  25. 113
      cranelift/filetests/filetests/isa/s390x/constants.clif
  26. 748
      cranelift/filetests/filetests/isa/s390x/conversions.clif
  27. 355
      cranelift/filetests/filetests/isa/s390x/div-traps.clif
  28. 711
      cranelift/filetests/filetests/isa/s390x/floating-point.clif
  29. 49
      cranelift/filetests/filetests/isa/s390x/heap_addr.clif
  30. 604
      cranelift/filetests/filetests/isa/s390x/icmp.clif
  31. 45
      cranelift/filetests/filetests/isa/s390x/jumptable.clif
  32. 258
      cranelift/filetests/filetests/isa/s390x/load-little.clif
  33. 264
      cranelift/filetests/filetests/isa/s390x/load.clif
  34. 79
      cranelift/filetests/filetests/isa/s390x/multivalue-ret.clif
  35. 101
      cranelift/filetests/filetests/isa/s390x/reftypes.clif
  36. 12
      cranelift/filetests/filetests/isa/s390x/saturating-ops.clif
  37. 461
      cranelift/filetests/filetests/isa/s390x/shift-rotate.clif
  38. 175
      cranelift/filetests/filetests/isa/s390x/stack-limit.clif
  39. 93
      cranelift/filetests/filetests/isa/s390x/stack.clif
  40. 281
      cranelift/filetests/filetests/isa/s390x/store-little.clif
  41. 296
      cranelift/filetests/filetests/isa/s390x/store.clif
  42. 54
      cranelift/filetests/filetests/isa/s390x/symbols.clif
  43. 91
      cranelift/filetests/filetests/isa/s390x/traps.clif

7
build.rs

@ -219,6 +219,9 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
_ => (),
},
"Cranelift" => match (testsuite, testname) {
// No simd support yet for s390x.
("simd", _) if platform_is_s390x() => return true,
("simd", _) if cfg!(feature = "old-x86-backend") => return true, // skip all SIMD tests on old backend.
// These are new instructions that are not really implemented in any backend.
("simd", "simd_i8x16_arith2")
@ -243,3 +246,7 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
fn platform_is_x64() -> bool {
env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "x86_64"
}
fn platform_is_s390x() -> bool {
env::var("CARGO_CFG_TARGET_ARCH").unwrap() == "s390x"
}

4
cranelift/codegen/Cargo.toml

@ -62,6 +62,7 @@ unwind = ["gimli"]
x86 = []
arm64 = []
riscv = []
s390x = []
arm32 = [] # Work-in-progress codegen backend for ARM.
# Stub feature that does nothing, for Cargo-features compatibility: the new
@ -75,7 +76,8 @@ old-x86-backend = []
all-arch = [
"x86",
"arm64",
"riscv"
"riscv",
"s390x"
]
# For dependent crates that want to serialize some parts of cranelift

7
cranelift/codegen/meta/src/isa/mod.rs

@ -6,6 +6,7 @@ use std::fmt;
mod arm32;
mod arm64;
mod riscv;
mod s390x;
pub(crate) mod x86;
/// Represents known ISA target.
@ -15,6 +16,7 @@ pub enum Isa {
X86,
Arm32,
Arm64,
S390x,
}
impl Isa {
@ -31,6 +33,7 @@ impl Isa {
match arch {
"riscv" => Some(Isa::Riscv),
"aarch64" => Some(Isa::Arm64),
"s390x" => Some(Isa::S390x),
x if ["x86_64", "i386", "i586", "i686"].contains(&x) => Some(Isa::X86),
x if x.starts_with("arm") || arch.starts_with("thumb") => Some(Isa::Arm32),
_ => None,
@ -39,7 +42,7 @@ impl Isa {
/// Returns all supported isa targets.
pub fn all() -> &'static [Isa] {
&[Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64]
&[Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64, Isa::S390x]
}
}
@ -51,6 +54,7 @@ impl fmt::Display for Isa {
Isa::X86 => write!(f, "x86"),
Isa::Arm32 => write!(f, "arm32"),
Isa::Arm64 => write!(f, "arm64"),
Isa::S390x => write!(f, "s390x"),
}
}
}
@ -62,6 +66,7 @@ pub(crate) fn define(isas: &[Isa], shared_defs: &mut SharedDefinitions) -> Vec<T
Isa::X86 => x86::define(shared_defs),
Isa::Arm32 => arm32::define(shared_defs),
Isa::Arm64 => arm64::define(shared_defs),
Isa::S390x => s390x::define(shared_defs),
})
.collect()
}

31
cranelift/codegen/meta/src/isa/s390x/mod.rs

@ -0,0 +1,31 @@
use crate::cdsl::cpu_modes::CpuMode;
use crate::cdsl::instructions::{InstructionGroupBuilder, InstructionPredicateMap};
use crate::cdsl::isa::TargetIsa;
use crate::cdsl::recipes::Recipes;
use crate::cdsl::regs::IsaRegsBuilder;
use crate::cdsl::settings::SettingGroupBuilder;
use crate::shared::Definitions as SharedDefinitions;
pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let inst_group = InstructionGroupBuilder::new(&mut shared_defs.all_instructions).build();
let settings = SettingGroupBuilder::new("s390x").build();
let regs = IsaRegsBuilder::new().build();
let recipes = Recipes::new();
let encodings_predicates = InstructionPredicateMap::new();
let mut mode = CpuMode::new("s390x");
let expand = shared_defs.transform_groups.by_name("expand");
mode.legalize_default(expand);
let cpu_modes = vec![mode];
TargetIsa::new(
"s390x",
inst_group,
settings,
regs,
recipes,
cpu_modes,
encodings_predicates,
)
}

3
cranelift/codegen/meta/src/lib.rs

@ -116,6 +116,9 @@ pub fn generate(
isa::Isa::Arm64 => {
// aarch64 doesn't have platform-specific settings.
}
isa::Isa::S390x => {
// s390x doesn't have platform-specific settings.
}
isa::Isa::Arm32 | isa::Isa::Riscv => todo!(),
}
}

4
cranelift/codegen/src/isa/mod.rs

@ -91,6 +91,9 @@ mod arm32;
#[cfg(feature = "arm64")]
pub(crate) mod aarch64;
#[cfg(feature = "s390x")]
mod s390x;
pub mod unwind;
mod call_conv;
@ -160,6 +163,7 @@ pub fn lookup_variant(triple: Triple, variant: BackendVariant) -> Result<Builder
}
(Architecture::Arm { .. }, _) => isa_builder!(arm32, (feature = "arm32"), triple),
(Architecture::Aarch64 { .. }, _) => isa_builder!(aarch64, (feature = "arm64"), triple),
(Architecture::S390x { .. }, _) => isa_builder!(s390x, (feature = "s390x"), triple),
_ => Err(LookupError::Unsupported),
}
}

770
cranelift/codegen/src/isa/s390x/abi.rs

@ -0,0 +1,770 @@
//! Implementation of a standard S390x ABI.
//!
//! This machine uses the "vanilla" ABI implementation from abi_impl.rs,
//! however a few details are different from the description there:
//!
//! - On s390x, the caller must provide a "register save area" of 160
//! bytes to any function it calls. The called function is free to use
//! this space for any purpose; usually to save callee-saved GPRs.
//! (Note that while this area is allocated by the caller, it is counted
//! as part of the callee's stack frame; in particular, the callee's CFA
//! is the top of the register save area, not the incoming SP value.)
//!
//! - Overflow arguments are passed on the stack starting immediately
//! above the register save area. On s390x, this space is allocated
//! only once directly in the prologue, using a size large enough to
//! hold overflow arguments for every call in the function.
//!
//! - On s390x we do not use a frame pointer register; instead, every
//! element of the stack frame is addressed via (constant) offsets
//! from the stack pointer. Note that due to the above (and because
//! there are no variable-sized stack allocations in cranelift), the
//! value of the stack pointer register never changes after the
//! initial allocation in the function prologue.
//!
//! Overall, the stack frame layout on s390x is as follows:
//!
//! ```plain
//! (high address)
//!
//! +---------------------------+
//! | ... |
//! CFA -----> | stack args |
//! +---------------------------+
//! | ... |
//! | 160 bytes reg save area |
//! SP at function entry -----> | (used to save GPRs) |
//! +---------------------------+
//! | ... |
//! | clobbered callee-saves |
//! | (used to save FPRs) |
//! unwind-frame base ----> | (alloc'd by prologue) |
//! +---------------------------+
//! | ... |
//! | spill slots |
//! | (accessed via nominal SP) |
//! | ... |
//! | stack slots |
//! | (accessed via nominal SP) |
//! nominal SP ---------------> | (alloc'd by prologue) |
//! +---------------------------+
//! | ... |
//! | args for call |
//! | outgoing reg save area |
//! SP during function ------> | (alloc'd by prologue) |
//! +---------------------------+
//!
//! (low address)
//! ```
use crate::ir;
use crate::ir::condcodes::IntCC;
use crate::ir::types;
use crate::ir::MemFlags;
use crate::ir::Type;
use crate::isa;
use crate::isa::s390x::inst::*;
use crate::isa::unwind::UnwindInst;
use crate::machinst::*;
use crate::settings;
use crate::{CodegenError, CodegenResult};
use alloc::boxed::Box;
use alloc::vec::Vec;
use regalloc::{RealReg, Reg, RegClass, Set, Writable};
use smallvec::{smallvec, SmallVec};
use std::convert::TryFrom;
// We use a generic implementation that factors out ABI commonalities.
/// Support for the S390x ABI from the callee side (within a function body).
pub type S390xABICallee = ABICalleeImpl<S390xMachineDeps>;
/// Support for the S390x ABI from the caller side (at a callsite).
pub type S390xABICaller = ABICallerImpl<S390xMachineDeps>;
/// ABI Register usage
fn in_int_reg(ty: Type) -> bool {
match ty {
types::I8 | types::I16 | types::I32 | types::I64 | types::R64 => true,
types::B1 | types::B8 | types::B16 | types::B32 | types::B64 => true,
_ => false,
}
}
fn in_flt_reg(ty: Type) -> bool {
match ty {
types::F32 | types::F64 => true,
_ => false,
}
}
fn get_intreg_for_arg(idx: usize) -> Option<Reg> {
match idx {
0 => Some(regs::gpr(2)),
1 => Some(regs::gpr(3)),
2 => Some(regs::gpr(4)),
3 => Some(regs::gpr(5)),
4 => Some(regs::gpr(6)),
_ => None,
}
}
fn get_fltreg_for_arg(idx: usize) -> Option<Reg> {
match idx {
0 => Some(regs::fpr(0)),
1 => Some(regs::fpr(2)),
2 => Some(regs::fpr(4)),
3 => Some(regs::fpr(6)),
_ => None,
}
}
fn get_intreg_for_ret(idx: usize) -> Option<Reg> {
match idx {
0 => Some(regs::gpr(2)),
// ABI extension to support multi-value returns:
1 => Some(regs::gpr(3)),
2 => Some(regs::gpr(4)),
3 => Some(regs::gpr(5)),
_ => None,
}
}
fn get_fltreg_for_ret(idx: usize) -> Option<Reg> {
match idx {
0 => Some(regs::fpr(0)),
// ABI extension to support multi-value returns:
1 => Some(regs::fpr(2)),
2 => Some(regs::fpr(4)),
3 => Some(regs::fpr(6)),
_ => None,
}
}
/// This is the limit for the size of argument and return-value areas on the
/// stack. We place a reasonable limit here to avoid integer overflow issues
/// with 32-bit arithmetic: for now, 128 MB.
static STACK_ARG_RET_SIZE_LIMIT: u64 = 128 * 1024 * 1024;
impl Into<MemArg> for StackAMode {
fn into(self) -> MemArg {
match self {
StackAMode::FPOffset(off, _ty) => MemArg::InitialSPOffset { off },
StackAMode::NominalSPOffset(off, _ty) => MemArg::NominalSPOffset { off },
StackAMode::SPOffset(off, _ty) => {
MemArg::reg_plus_off(stack_reg(), off, MemFlags::trusted())
}
}
}
}
/// S390x-specific ABI behavior. This struct just serves as an implementation
/// point for the trait; it is never actually instantiated.
pub struct S390xMachineDeps;
impl ABIMachineSpec for S390xMachineDeps {
type I = Inst;
fn word_bits() -> u32 {
64
}
/// Return required stack alignment in bytes.
fn stack_align(_call_conv: isa::CallConv) -> u32 {
8
}
fn compute_arg_locs(
call_conv: isa::CallConv,
_flags: &settings::Flags,
params: &[ir::AbiParam],
args_or_rets: ArgsOrRets,
add_ret_area_ptr: bool,
) -> CodegenResult<(Vec<ABIArg>, i64, Option<usize>)> {
let mut next_gpr = 0;
let mut next_fpr = 0;
let mut next_stack: u64 = 0;
let mut ret = vec![];
if args_or_rets == ArgsOrRets::Args {
next_stack = 160;
}
for i in 0..params.len() {
let param = &params[i];
// Validate "purpose".
match &param.purpose {
&ir::ArgumentPurpose::VMContext
| &ir::ArgumentPurpose::Normal
| &ir::ArgumentPurpose::StackLimit
| &ir::ArgumentPurpose::SignatureId => {}
_ => panic!(
"Unsupported argument purpose {:?} in signature: {:?}",
param.purpose, params
),
}
let intreg = in_int_reg(param.value_type);
let fltreg = in_flt_reg(param.value_type);
debug_assert!(intreg || fltreg);
debug_assert!(!(intreg && fltreg));
let (next_reg, candidate) = if intreg {
let candidate = match args_or_rets {
ArgsOrRets::Args => get_intreg_for_arg(next_gpr),
ArgsOrRets::Rets => get_intreg_for_ret(next_gpr),
};
(&mut next_gpr, candidate)
} else {
let candidate = match args_or_rets {
ArgsOrRets::Args => get_fltreg_for_arg(next_fpr),
ArgsOrRets::Rets => get_fltreg_for_ret(next_fpr),
};
(&mut next_fpr, candidate)
};
// In the Wasmtime ABI only the first return value can be in a register.
let candidate =
if call_conv.extends_wasmtime() && args_or_rets == ArgsOrRets::Rets && i > 0 {
None
} else {
candidate
};
if let Some(reg) = candidate {
ret.push(ABIArg::reg(
reg.to_real_reg(),
param.value_type,
param.extension,
param.purpose,
));
*next_reg += 1;
} else {
// Compute size. Every argument or return value takes a slot of
// at least 8 bytes, except for return values in the Wasmtime ABI.
let size = (ty_bits(param.value_type) / 8) as u64;
let slot_size = if call_conv.extends_wasmtime() && args_or_rets == ArgsOrRets::Rets
{
size
} else {
std::cmp::max(size, 8)
};
// Align the stack slot.
debug_assert!(slot_size.is_power_of_two());
next_stack = align_to(next_stack, slot_size);
// If the type is actually of smaller size (and the argument
// was not extended), it is passed right-aligned.
let offset = if size < slot_size && param.extension == ir::ArgumentExtension::None {
slot_size - size
} else {
0
};
ret.push(ABIArg::stack(
(next_stack + offset) as i64,
param.value_type,
param.extension,
param.purpose,
));
next_stack += slot_size;
}
}
next_stack = align_to(next_stack, 8);
let extra_arg = if add_ret_area_ptr {
debug_assert!(args_or_rets == ArgsOrRets::Args);
if let Some(reg) = get_intreg_for_arg(next_gpr) {
ret.push(ABIArg::reg(
reg.to_real_reg(),
types::I64,
ir::ArgumentExtension::None,
ir::ArgumentPurpose::Normal,
));
} else {
ret.push(ABIArg::stack(
next_stack as i64,
types::I64,
ir::ArgumentExtension::None,
ir::ArgumentPurpose::Normal,
));
next_stack += 8;
}
Some(ret.len() - 1)
} else {
None
};
// To avoid overflow issues, limit the arg/return size to something
// reasonable -- here, 128 MB.
if next_stack > STACK_ARG_RET_SIZE_LIMIT {
return Err(CodegenError::ImplLimitExceeded);
}
Ok((ret, next_stack as i64, extra_arg))
}
fn fp_to_arg_offset(_call_conv: isa::CallConv, _flags: &settings::Flags) -> i64 {
0
}
fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Inst {
Inst::gen_load(into_reg, mem.into(), ty)
}
fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Inst {
Inst::gen_store(mem.into(), from_reg, ty)
}
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {
Inst::gen_move(to_reg, from_reg, ty)
}
fn gen_extend(
to_reg: Writable<Reg>,
from_reg: Reg,
signed: bool,
from_bits: u8,
to_bits: u8,
) -> Inst {
assert!(from_bits < to_bits);
Inst::Extend {
rd: to_reg,
rn: from_reg,
signed,
from_bits,
to_bits,
}
}
fn gen_ret() -> Inst {
Inst::Ret { link: gpr(14) }
}
fn gen_add_imm(into_reg: Writable<Reg>, from_reg: Reg, imm: u32) -> SmallInstVec<Inst> {
let mut insts = SmallVec::new();
if let Some(imm) = UImm12::maybe_from_u64(imm as u64) {
insts.push(Inst::LoadAddr {
rd: into_reg,
mem: MemArg::BXD12 {
base: from_reg,
index: zero_reg(),
disp: imm,
flags: MemFlags::trusted(),
},
});
} else if let Some(imm) = SImm20::maybe_from_i64(imm as i64) {
insts.push(Inst::LoadAddr {
rd: into_reg,
mem: MemArg::BXD20 {
base: from_reg,
index: zero_reg(),
disp: imm,
flags: MemFlags::trusted(),
},
});
} else {
if from_reg != into_reg.to_reg() {
insts.push(Inst::mov64(into_reg, from_reg));
}
insts.push(Inst::AluRUImm32 {
alu_op: ALUOp::Add64,
rd: into_reg,
imm,
});
}
insts
}
fn gen_stack_lower_bound_trap(limit_reg: Reg) -> SmallInstVec<Inst> {
let mut insts = SmallVec::new();
insts.push(Inst::CmpTrapRR {
op: CmpOp::CmpL64,
rn: stack_reg(),
rm: limit_reg,
cond: Cond::from_intcc(IntCC::UnsignedLessThanOrEqual),
trap_code: ir::TrapCode::StackOverflow,
});
insts
}
fn gen_epilogue_placeholder() -> Inst {
Inst::EpiloguePlaceholder
}
fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>, _ty: Type) -> Inst {
let mem = mem.into();
Inst::LoadAddr { rd: into_reg, mem }
}
fn get_stacklimit_reg() -> Reg {
spilltmp_reg()
}
fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
let mem = MemArg::reg_plus_off(base, offset.into(), MemFlags::trusted());
Inst::gen_load(into_reg, mem, ty)
}
fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
let mem = MemArg::reg_plus_off(base, offset.into(), MemFlags::trusted());
Inst::gen_store(mem, from_reg, ty)
}
fn gen_sp_reg_adjust(imm: i32) -> SmallInstVec<Inst> {
if imm == 0 {
return SmallVec::new();
}
let mut insts = SmallVec::new();
if let Ok(imm) = i16::try_from(imm) {
insts.push(Inst::AluRSImm16 {
alu_op: ALUOp::Add64,
rd: writable_stack_reg(),
imm,
});
} else {
insts.push(Inst::AluRSImm32 {
alu_op: ALUOp::Add64,
rd: writable_stack_reg(),
imm,
});
}
insts
}
fn gen_nominal_sp_adj(offset: i32) -> Inst {
Inst::VirtualSPOffsetAdj {
offset: offset.into(),
}
}
fn gen_prologue_frame_setup(_flags: &settings::Flags) -> SmallInstVec<Inst> {
SmallVec::new()
}
fn gen_epilogue_frame_restore(_flags: &settings::Flags) -> SmallInstVec<Inst> {
SmallVec::new()
}
fn gen_probestack(_: u32) -> SmallInstVec<Self::I> {
// TODO: implement if we ever require stack probes on an s390x host
// (unlikely unless Lucet is ported)
smallvec![]
}
// Returns stack bytes used as well as instructions. Does not adjust
// nominal SP offset; abi_impl generic code will do that.
fn gen_clobber_save(
call_conv: isa::CallConv,
flags: &settings::Flags,
clobbers: &Set<Writable<RealReg>>,
fixed_frame_storage_size: u32,
outgoing_args_size: u32,
) -> (u64, SmallVec<[Inst; 16]>) {
let mut insts = SmallVec::new();
// Collect clobbered registers.
let (clobbered_gpr, clobbered_fpr) = get_regs_saved_in_prologue(call_conv, clobbers);
let mut first_clobbered_gpr = 16;
for reg in clobbered_gpr {
let enc = reg.to_reg().get_hw_encoding();
if enc < first_clobbered_gpr {
first_clobbered_gpr = enc;
}
}
let clobber_size = clobbered_fpr.len() * 8;
if flags.unwind_info() {
insts.push(Inst::Unwind {
inst: UnwindInst::DefineNewFrame {
offset_upward_to_caller_sp: 160,
offset_downward_to_clobbers: clobber_size as u32,
},
});
}
// Use STMG to save clobbered GPRs into save area.
if first_clobbered_gpr < 16 {
let offset = 8 * first_clobbered_gpr as i64;
insts.push(Inst::StoreMultiple64 {
rt: gpr(first_clobbered_gpr as u8),
rt2: gpr(15),
addr_reg: stack_reg(),
addr_off: SImm20::maybe_from_i64(offset).unwrap(),
});
}
if flags.unwind_info() {
for i in first_clobbered_gpr..16 {
insts.push(Inst::Unwind {
inst: UnwindInst::SaveReg {
clobber_offset: clobber_size as u32 + (i * 8) as u32,
reg: gpr(i as u8).to_real_reg(),
},
});
}
}
// Decrement stack pointer.
let stack_size =
outgoing_args_size as i32 + clobber_size as i32 + fixed_frame_storage_size as i32;
insts.extend(Self::gen_sp_reg_adjust(-stack_size));
if flags.unwind_info() {
insts.push(Inst::Unwind {
inst: UnwindInst::StackAlloc {
size: stack_size as u32,
},
});
}
let sp_adj = outgoing_args_size as i32;
if sp_adj > 0 {
insts.push(Self::gen_nominal_sp_adj(sp_adj));
}
// Save FPRs.
for (i, reg) in clobbered_fpr.iter().enumerate() {
insts.push(Inst::FpuStore64 {
rd: reg.to_reg().to_reg(),
mem: MemArg::reg_plus_off(
stack_reg(),
(i * 8) as i64 + outgoing_args_size as i64 + fixed_frame_storage_size as i64,
MemFlags::trusted(),
),
});
if flags.unwind_info() {
insts.push(Inst::Unwind {
inst: UnwindInst::SaveReg {
clobber_offset: (i * 8) as u32,
reg: reg.to_reg(),
},
});
}
}
(clobber_size as u64, insts)
}
fn gen_clobber_restore(
call_conv: isa::CallConv,
_: &settings::Flags,
clobbers: &Set<Writable<RealReg>>,
fixed_frame_storage_size: u32,
outgoing_args_size: u32,
) -> SmallVec<[Inst; 16]> {
let mut insts = SmallVec::new();
// Collect clobbered registers.
let (clobbered_gpr, clobbered_fpr) = get_regs_saved_in_prologue(call_conv, clobbers);
let mut first_clobbered_gpr = 16;
for reg in clobbered_gpr {
let enc = reg.to_reg().get_hw_encoding();
if enc < first_clobbered_gpr {
first_clobbered_gpr = enc;
}
}
let clobber_size = clobbered_fpr.len() * 8;
// Restore FPRs.
for (i, reg) in clobbered_fpr.iter().enumerate() {
insts.push(Inst::FpuLoad64 {
rd: Writable::from_reg(reg.to_reg().to_reg()),
mem: MemArg::reg_plus_off(
stack_reg(),
(i * 8) as i64 + outgoing_args_size as i64 + fixed_frame_storage_size as i64,
MemFlags::trusted(),
),
});
}
// Increment stack pointer unless it will be restored implicitly.
let stack_size =
outgoing_args_size as i32 + clobber_size as i32 + fixed_frame_storage_size as i32;
let implicit_sp_restore = first_clobbered_gpr < 16
&& SImm20::maybe_from_i64(8 * first_clobbered_gpr as i64 + stack_size as i64).is_some();
if !implicit_sp_restore {
insts.extend(Self::gen_sp_reg_adjust(stack_size));
}
// Use LMG to restore clobbered GPRs from save area.
if first_clobbered_gpr < 16 {
let mut offset = 8 * first_clobbered_gpr as i64;
if implicit_sp_restore {
offset += stack_size as i64;
}
insts.push(Inst::LoadMultiple64 {
rt: writable_gpr(first_clobbered_gpr as u8),
rt2: writable_gpr(15),
addr_reg: stack_reg(),
addr_off: SImm20::maybe_from_i64(offset).unwrap(),
});
}
insts
}
fn gen_call(
dest: &CallDest,
uses: Vec<Reg>,
defs: Vec<Writable<Reg>>,
opcode: ir::Opcode,
tmp: Writable<Reg>,
_callee_conv: isa::CallConv,
_caller_conv: isa::CallConv,
) -> SmallVec<[(InstIsSafepoint, Inst); 2]> {
let mut insts = SmallVec::new();
match &dest {
&CallDest::ExtName(ref name, RelocDistance::Near) => insts.push((
InstIsSafepoint::Yes,
Inst::Call {
link: writable_gpr(14),
info: Box::new(CallInfo {
dest: name.clone(),
uses,
defs,
opcode,
}),
},
)),
&CallDest::ExtName(ref name, RelocDistance::Far) => {
insts.push((
InstIsSafepoint::No,
Inst::LoadExtNameFar {
rd: tmp,
name: Box::new(name.clone()),
offset: 0,
},
));
insts.push((
InstIsSafepoint::Yes,
Inst::CallInd {
link: writable_gpr(14),
info: Box::new(CallIndInfo {
rn: tmp.to_reg(),
uses,
defs,
opcode,
}),
},
));
}
&CallDest::Reg(reg) => insts.push((
InstIsSafepoint::Yes,
Inst::CallInd {
link: writable_gpr(14),
info: Box::new(CallIndInfo {
rn: *reg,
uses,
defs,
opcode,
}),
},
)),
}
insts
}
fn gen_memcpy(
_call_conv: isa::CallConv,
_dst: Reg,
_src: Reg,
_size: usize,
) -> SmallVec<[Self::I; 8]> {
unimplemented!("StructArgs not implemented for S390X yet");
}
fn get_number_of_spillslots_for_value(rc: RegClass, ty: Type) -> u32 {
// We allocate in terms of 8-byte slots.
match (rc, ty) {
(RegClass::I64, _) => 1,
(RegClass::F64, _) => 1,
_ => panic!("Unexpected register class!"),
}
}
/// Get the current virtual-SP offset from an instruction-emission state.
fn get_virtual_sp_offset_from_state(s: &EmitState) -> i64 {
s.virtual_sp_offset
}
/// Get the nominal-SP-to-FP offset from an instruction-emission state.
fn get_nominal_sp_to_fp(s: &EmitState) -> i64 {
s.initial_sp_offset
}
fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> Vec<Writable<Reg>> {
let mut caller_saved = Vec::new();
for i in 0..15 {
let x = writable_gpr(i);
if is_reg_clobbered_by_call(call_conv_of_callee, x.to_reg().to_real_reg()) {
caller_saved.push(x);
}
}
for i in 0..15 {
let v = writable_fpr(i);
if is_reg_clobbered_by_call(call_conv_of_callee, v.to_reg().to_real_reg()) {
caller_saved.push(v);
}
}
caller_saved
}
fn get_ext_mode(
_call_conv: isa::CallConv,
specified: ir::ArgumentExtension,
) -> ir::ArgumentExtension {
specified
}
}
fn is_reg_saved_in_prologue(_call_conv: isa::CallConv, r: RealReg) -> bool {
match r.get_class() {
RegClass::I64 => {
// r6 - r15 inclusive are callee-saves.
r.get_hw_encoding() >= 6 && r.get_hw_encoding() <= 15
}
RegClass::F64 => {
// f8 - f15 inclusive are callee-saves.
r.get_hw_encoding() >= 8 && r.get_hw_encoding() <= 15
}
_ => panic!("Unexpected RegClass"),
}
}
fn get_regs_saved_in_prologue(
call_conv: isa::CallConv,
regs: &Set<Writable<RealReg>>,
) -> (Vec<Writable<RealReg>>, Vec<Writable<RealReg>>) {
let mut int_saves = vec![];
let mut fpr_saves = vec![];
for &reg in regs.iter() {
if is_reg_saved_in_prologue(call_conv, reg.to_reg()) {
match reg.to_reg().get_class() {
RegClass::I64 => int_saves.push(reg),
RegClass::F64 => fpr_saves.push(reg),
_ => panic!("Unexpected RegClass"),
}
}
}
// Sort registers for deterministic code output.
int_saves.sort_by_key(|r| r.to_reg().get_index());
fpr_saves.sort_by_key(|r| r.to_reg().get_index());
(int_saves, fpr_saves)
}
fn is_reg_clobbered_by_call(_call_conv: isa::CallConv, r: RealReg) -> bool {
match r.get_class() {
RegClass::I64 => {
// r0 - r5 inclusive are caller-saves.
r.get_hw_encoding() <= 5
}
RegClass::F64 => {
// f0 - f7 inclusive are caller-saves.
r.get_hw_encoding() <= 7
}
_ => panic!("Unexpected RegClass"),
}
}

317
cranelift/codegen/src/isa/s390x/inst/args.rs

@ -0,0 +1,317 @@
//! S390x ISA definitions: instruction arguments.
// Some variants are never constructed, but we still want them as options in the future.
#![allow(dead_code)]
use crate::ir::condcodes::{FloatCC, IntCC};
use crate::ir::MemFlags;
use crate::isa::s390x::inst::*;
use crate::machinst::MachLabel;
use regalloc::{PrettyPrint, RealRegUniverse, 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: BranchTarget },
/// 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()
}
}
//=============================================================================
// 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
}
}
/// A branch target. Either unresolved (basic-block index) or resolved (offset
/// from end of current instruction).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BranchTarget {
/// An unresolved reference to a Label, as passed into
/// `lower_branch_group()`.
Label(MachLabel),
/// A fixed PC offset.
ResolvedOffset(i32),
}
impl BranchTarget {
/// Return the target's label, if it is a label-based target.
pub fn as_label(self) -> Option<MachLabel> {
match self {
BranchTarget::Label(l) => Some(l),
_ => None,
}
}
/// Return the target's offset, if specified, or zero if label-based.
pub fn as_ri_offset_or_zero(self) -> u16 {
let off = match self {
BranchTarget::ResolvedOffset(off) => off >> 1,
_ => 0,
};
assert!(off <= 0x7fff);
assert!(off >= -0x8000);
off as u16
}
/// Return the target's offset, if specified, or zero if label-based.
pub fn as_ril_offset_or_zero(self) -> u32 {
let off = match self {
BranchTarget::ResolvedOffset(off) => off >> 1,
_ => 0,
};
off as u32
}
}
impl PrettyPrint for MemArg {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
match self {
&MemArg::BXD12 {
base, index, disp, ..
} => {
if base != zero_reg() {
if index != zero_reg() {
format!(
"{}({},{})",
disp.show_rru(mb_rru),
index.show_rru(mb_rru),
base.show_rru(mb_rru)
)
} else {
format!("{}({})", disp.show_rru(mb_rru), base.show_rru(mb_rru))
}
} else {
if index != zero_reg() {
format!("{}({},)", disp.show_rru(mb_rru), index.show_rru(mb_rru))
} else {
format!("{}", disp.show_rru(mb_rru))
}
}
}
&MemArg::BXD20 {
base, index, disp, ..
} => {
if base != zero_reg() {
if index != zero_reg() {
format!(
"{}({},{})",
disp.show_rru(mb_rru),
index.show_rru(mb_rru),
base.show_rru(mb_rru)
)
} else {
format!("{}({})", disp.show_rru(mb_rru), base.show_rru(mb_rru))
}
} else {
if index != zero_reg() {
format!("{}({},)", disp.show_rru(mb_rru), index.show_rru(mb_rru))
} else {
format!("{}", disp.show_rru(mb_rru))
}
}
}
&MemArg::Label { ref target } => target.show_rru(mb_rru),
&MemArg::Symbol {
ref name, offset, ..
} => format!("{} + {}", name, 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 show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> 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()
}
}
impl PrettyPrint for BranchTarget {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
match self {
&BranchTarget::Label(label) => format!("label{:?}", label.get()),
&BranchTarget::ResolvedOffset(off) => format!("{}", off),
}
}
}

1965
cranelift/codegen/src/isa/s390x/inst/emit.rs

File diff suppressed because it is too large

7140
cranelift/codegen/src/isa/s390x/inst/emit_tests.rs

File diff suppressed because it is too large

231
cranelift/codegen/src/isa/s390x/inst/imms.rs

@ -0,0 +1,231 @@
//! S390x ISA definitions: immediate constants.
use regalloc::{PrettyPrint, RealRegUniverse};
use std::string::String;
/// An unsigned 12-bit immediate.
#[derive(Clone, Copy, Debug)]
pub struct UImm12 {
/// The value.
value: u16,
}
impl UImm12 {
pub fn maybe_from_u64(value: u64) -> Option<UImm12> {
if value < 4096 {
Some(UImm12 {
value: value as u16,
})
} else {
None
}
}
/// Create a zero immediate of this format.
pub fn zero() -> UImm12 {
UImm12 { value: 0 }
}
/// Bits for encoding.
pub fn bits(&self) -> u32 {
u32::from(self.value)
}
}
/// A signed 20-bit immediate.
#[derive(Clone, Copy, Debug)]
pub struct SImm20 {
/// The value.
value: i32,
}
impl SImm20 {
pub fn maybe_from_i64(value: i64) -> Option<SImm20> {
if value >= -524288 && value < 524288 {
Some(SImm20 {
value: value as i32,
})
} else {
None
}
}
pub fn from_uimm12(value: UImm12) -> SImm20 {
SImm20 {
value: value.bits() as i32,
}
}
/// Create a zero immediate of this format.
pub fn zero() -> SImm20 {
SImm20 { value: 0 }
}
/// Bits for encoding.
pub fn bits(&self) -> u32 {
let encoded: u32 = self.value as u32;
encoded & 0xfffff
}
}
/// A 16-bit immediate with a {0,16,32,48}-bit shift.
#[derive(Clone, Copy, Debug)]
pub struct UImm16Shifted {
/// The value.
pub bits: u16,
/// Result is `bits` shifted 16*shift bits to the left.
pub shift: u8,
}
impl UImm16Shifted {
/// Construct a UImm16Shifted from an arbitrary 64-bit constant if possible.
pub fn maybe_from_u64(value: u64) -> Option<UImm16Shifted> {
let mask0 = 0x0000_0000_0000_ffffu64;
let mask1 = 0x0000_0000_ffff_0000u64;
let mask2 = 0x0000_ffff_0000_0000u64;
let mask3 = 0xffff_0000_0000_0000u64;
if value == (value & mask0) {
return Some(UImm16Shifted {
bits: (value & mask0) as u16,
shift: 0,
});
}
if value == (value & mask1) {
return Some(UImm16Shifted {
bits: ((value >> 16) & mask0) as u16,
shift: 1,
});
}
if value == (value & mask2) {
return Some(UImm16Shifted {
bits: ((value >> 32) & mask0) as u16,
shift: 2,
});
}
if value == (value & mask3) {
return Some(UImm16Shifted {
bits: ((value >> 48) & mask0) as u16,
shift: 3,
});
}
None
}
pub fn maybe_with_shift(imm: u16, shift: u8) -> Option<UImm16Shifted> {
let shift_enc = shift / 16;
if shift_enc > 3 {
None
} else {
Some(UImm16Shifted {
bits: imm,
shift: shift_enc,
})
}
}
pub fn negate_bits(&self) -> UImm16Shifted {
UImm16Shifted {
bits: !self.bits,
shift: self.shift,
}
}
/// Returns the value that this constant represents.
pub fn value(&self) -> u64 {
(self.bits as u64) << (16 * self.shift)
}
}
/// A 32-bit immediate with a {0,32}-bit shift.
#[derive(Clone, Copy, Debug)]
pub struct UImm32Shifted {
/// The value.
pub bits: u32,
/// Result is `bits` shifted 32*shift bits to the left.
pub shift: u8,
}
impl UImm32Shifted {
/// Construct a UImm32Shifted from an arbitrary 64-bit constant if possible.
pub fn maybe_from_u64(value: u64) -> Option<UImm32Shifted> {
let mask0 = 0x0000_0000_ffff_ffffu64;
let mask1 = 0xffff_ffff_0000_0000u64;
if value == (value & mask0) {
return Some(UImm32Shifted {
bits: (value & mask0) as u32,
shift: 0,
});
}
if value == (value & mask1) {
return Some(UImm32Shifted {
bits: ((value >> 32) & mask0) as u32,
shift: 1,
});
}
None
}
pub fn maybe_with_shift(imm: u32, shift: u8) -> Option<UImm32Shifted> {
let shift_enc = shift / 32;
if shift_enc > 3 {
None
} else {
Some(UImm32Shifted {
bits: imm,
shift: shift_enc,
})
}
}
pub fn from_uimm16shifted(value: UImm16Shifted) -> UImm32Shifted {
if value.shift % 2 == 0 {
UImm32Shifted {
bits: value.bits as u32,
shift: value.shift / 2,
}
} else {
UImm32Shifted {
bits: (value.bits as u32) << 16,
shift: value.shift / 2,
}
}
}
pub fn negate_bits(&self) -> UImm32Shifted {
UImm32Shifted {
bits: !self.bits,
shift: self.shift,
}
}
/// Returns the value that this constant represents.
pub fn value(&self) -> u64 {
(self.bits as u64) << (32 * self.shift)
}
}
impl PrettyPrint for UImm12 {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("{}", self.value)
}
}
impl PrettyPrint for SImm20 {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("{}", self.value)
}
}
impl PrettyPrint for UImm16Shifted {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("{}", self.bits)
}
}
impl PrettyPrint for UImm32Shifted {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
format!("{}", self.bits)
}
}

3411
cranelift/codegen/src/isa/s390x/inst/mod.rs

File diff suppressed because it is too large

168
cranelift/codegen/src/isa/s390x/inst/regs.rs

@ -0,0 +1,168 @@
//! S390x ISA definitions: registers.
use crate::settings;
use regalloc::{RealRegUniverse, Reg, RegClass, RegClassInfo, Writable, NUM_REG_CLASSES};
//=============================================================================
// Registers, the Universe thereof, and printing
#[rustfmt::skip]
const GPR_INDICES: [u8; 16] = [
// r0 and r1 reserved
30, 31,
// r2 - r5 call-clobbered
16, 17, 18, 19,
// r6 - r14 call-saved (order reversed)
28, 27, 26, 25, 24, 23, 22, 21, 20,
// r15 (SP)
29,
];
#[rustfmt::skip]
const FPR_INDICES: [u8; 16] = [
// f0 - f7 as pairs
0, 4, 1, 5, 2, 6, 3, 7,
// f8 - f15 as pairs
8, 12, 9, 13, 10, 14, 11, 15,
];
/// Get a reference to a GPR (integer register).
pub fn gpr(num: u8) -> Reg {
assert!(num < 16);
Reg::new_real(
RegClass::I64,
/* enc = */ num,
/* index = */ GPR_INDICES[num as usize],
)
}
/// Get a writable reference to a GPR.
pub fn writable_gpr(num: u8) -> Writable<Reg> {
Writable::from_reg(gpr(num))
}
/// Get a reference to a FPR (floating-point register).
pub fn fpr(num: u8) -> Reg {
assert!(num < 16);
Reg::new_real(
RegClass::F64,
/* enc = */ num,
/* index = */ FPR_INDICES[num as usize],
)
}
/// Get a writable reference to a V-register.
pub fn writable_fpr(num: u8) -> Writable<Reg> {
Writable::from_reg(fpr(num))
}
/// Get a reference to the stack-pointer register.
pub fn stack_reg() -> Reg {
gpr(15)
}
/// Get a writable reference to the stack-pointer register.
pub fn writable_stack_reg() -> Writable<Reg> {
Writable::from_reg(stack_reg())
}
/// Get a reference to the first temporary, sometimes "spill temporary", register. This register is
/// used to compute the address of a spill slot when a direct offset addressing mode from FP is not
/// sufficient (+/- 2^11 words). We exclude this register from regalloc and reserve it for this
/// purpose for simplicity; otherwise we need a multi-stage analysis where we first determine how
/// many spill slots we have, then perhaps remove the reg from the pool and recompute regalloc.
///
/// We use r1 for this because it's a scratch register but is slightly special (used for linker
/// veneers). We're free to use it as long as we don't expect it to live through call instructions.
pub fn spilltmp_reg() -> Reg {
gpr(1)
}
/// Get a writable reference to the spilltmp reg.
pub fn writable_spilltmp_reg() -> Writable<Reg> {
Writable::from_reg(spilltmp_reg())
}
pub fn zero_reg() -> Reg {
gpr(0)
}
/// Create the register universe for AArch64.
pub fn create_reg_universe(_flags: &settings::Flags) -> RealRegUniverse {
let mut regs = vec![];
let mut allocable_by_class = [None; NUM_REG_CLASSES];
// Numbering Scheme: we put FPRs first, then GPRs. The GPRs exclude several registers:
// r0 (we cannot use this for addressing // FIXME regalloc)
// r1 (spilltmp)
// r15 (stack pointer)
// FPRs.
let mut base = regs.len();
regs.push((fpr(0).to_real_reg(), "%f0".into()));
regs.push((fpr(2).to_real_reg(), "%f2".into()));
regs.push((fpr(4).to_real_reg(), "%f4".into()));
regs.push((fpr(6).to_real_reg(), "%f6".into()));
regs.push((fpr(1).to_real_reg(), "%f1".into()));
regs.push((fpr(3).to_real_reg(), "%f3".into()));
regs.push((fpr(5).to_real_reg(), "%f5".into()));
regs.push((fpr(7).to_real_reg(), "%f7".into()));
regs.push((fpr(8).to_real_reg(), "%f8".into()));
regs.push((fpr(10).to_real_reg(), "%f10".into()));
regs.push((fpr(12).to_real_reg(), "%f12".into()));
regs.push((fpr(14).to_real_reg(), "%f14".into()));
regs.push((fpr(9).to_real_reg(), "%f9".into()));
regs.push((fpr(11).to_real_reg(), "%f11".into()));
regs.push((fpr(13).to_real_reg(), "%f13".into()));
regs.push((fpr(15).to_real_reg(), "%f15".into()));
allocable_by_class[RegClass::F64.rc_to_usize()] = Some(RegClassInfo {
first: base,
last: regs.len() - 1,
suggested_scratch: Some(fpr(1).get_index()),
});
// Caller-saved GPRs in the SystemV s390x ABI.
base = regs.len();
regs.push((gpr(2).to_real_reg(), "%r2".into()));
regs.push((gpr(3).to_real_reg(), "%r3".into()));
regs.push((gpr(4).to_real_reg(), "%r4".into()));
regs.push((gpr(5).to_real_reg(), "%r5".into()));
// Callee-saved GPRs in the SystemV s390x ABI.
// We start from r14 downwards in an attempt to allow the
// prolog to use as short a STMG as possible.
regs.push((gpr(14).to_real_reg(), "%r14".into()));
regs.push((gpr(13).to_real_reg(), "%r13".into()));
regs.push((gpr(12).to_real_reg(), "%r12".into()));
regs.push((gpr(11).to_real_reg(), "%r11".into()));
regs.push((gpr(10).to_real_reg(), "%r10".into()));
regs.push((gpr(9).to_real_reg(), "%r9".into()));
regs.push((gpr(8).to_real_reg(), "%r8".into()));
regs.push((gpr(7).to_real_reg(), "%r7".into()));
regs.push((gpr(6).to_real_reg(), "%r6".into()));
allocable_by_class[RegClass::I64.rc_to_usize()] = Some(RegClassInfo {
first: base,
last: regs.len() - 1,
suggested_scratch: Some(gpr(13).get_index()),
});
// Other regs, not available to the allocator.
let allocable = regs.len();
regs.push((gpr(15).to_real_reg(), "%r15".into()));
regs.push((gpr(0).to_real_reg(), "%r0".into()));
regs.push((gpr(1).to_real_reg(), "%r1".into()));
// Assert sanity: the indices in the register structs must match their
// actual indices in the array.
for (i, reg) in regs.iter().enumerate() {
assert_eq!(i, reg.0.get_index());
}
RealRegUniverse {
regs,
allocable,
allocable_by_class,
}
}

2
cranelift/codegen/src/isa/s390x/inst/unwind.rs

@ -0,0 +1,2 @@
#[cfg(feature = "unwind")]
pub(crate) mod systemv;

197
cranelift/codegen/src/isa/s390x/inst/unwind/systemv.rs

@ -0,0 +1,197 @@
//! Unwind information for System V ABI (s390x).
use crate::isa::unwind::systemv::RegisterMappingError;
use gimli::{write::CommonInformationEntry, Encoding, Format, Register};
use regalloc::{Reg, RegClass};
/// Creates a new s390x common information entry (CIE).
pub fn create_cie() -> CommonInformationEntry {
use gimli::write::CallFrameInstruction;
let mut entry = CommonInformationEntry::new(
Encoding {
address_size: 8,
format: Format::Dwarf32,
version: 1,
},
1, // Code alignment factor
-8, // Data alignment factor
Register(14), // Return address column - register %r14
);
// Every frame will start with the call frame address (CFA) at %r15 + 160.
entry.add_instruction(CallFrameInstruction::Cfa(Register(15), 160));
entry
}
/// Map Cranelift registers to their corresponding Gimli registers.
pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
const GPR_MAP: [gimli::Register; 16] = [
Register(0),
Register(1),
Register(2),
Register(3),
Register(4),
Register(5),
Register(6),
Register(7),
Register(8),
Register(9),
Register(10),
Register(11),
Register(12),
Register(13),
Register(14),
Register(15),
];
const FPR_MAP: [gimli::Register; 16] = [
Register(16),
Register(20),
Register(17),
Register(21),
Register(18),
Register(22),
Register(19),
Register(23),
Register(24),
Register(28),
Register(25),
Register(29),
Register(26),
Register(30),
Register(27),
Register(31),
];
match reg.get_class() {
RegClass::I64 => Ok(GPR_MAP[reg.get_hw_encoding() as usize]),
RegClass::F64 => Ok(FPR_MAP[reg.get_hw_encoding() as usize]),
_ => Err(RegisterMappingError::UnsupportedRegisterBank("class?")),
}
}
pub(crate) struct RegisterMapper;
impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
Ok(map_reg(reg)?.0)
}
fn sp(&self) -> u16 {
Register(15).0
}
}
#[cfg(test)]
mod tests {
use crate::cursor::{Cursor, FuncCursor};
use crate::ir::{
types, AbiParam, ExternalName, Function, InstBuilder, Signature, StackSlotData,
StackSlotKind,
};
use crate::isa::{lookup, CallConv};
use crate::settings::{builder, Flags};
use crate::Context;
use gimli::write::Address;
use std::str::FromStr;
use target_lexicon::triple;
#[test]
fn test_simple_func() {
let isa = lookup(triple!("s390x"))
.expect("expect s390x ISA")
.finish(Flags::new(builder()));
let mut context = Context::for_function(create_function(
CallConv::SystemV,
Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
));
context.compile(&*isa).expect("expected compilation");
let fde = match context
.create_unwind_info(isa.as_ref())
.expect("can create unwind info")
{
Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
info.to_fde(Address::Constant(1234))
}
_ => panic!("expected unwind information"),
};
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 10, lsda: None, instructions: [(4, CfaOffset(224))] }");
}
fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
let mut func =
Function::with_name_signature(ExternalName::user(0, 0), Signature::new(call_conv));
let block0 = func.dfg.make_block();
let mut pos = FuncCursor::new(&mut func);
pos.insert_block(block0);
pos.ins().return_(&[]);
if let Some(stack_slot) = stack_slot {
func.stack_slots.push(stack_slot);
}
func
}
#[test]
fn test_multi_return_func() {
let isa = lookup(triple!("s390x"))
.expect("expect s390x ISA")
.finish(Flags::new(builder()));
let mut context = Context::for_function(create_multi_return_function(
CallConv::SystemV,
Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
));
context.compile(&*isa).expect("expected compilation");
let fde = match context
.create_unwind_info(isa.as_ref())
.expect("can create unwind info")
{
Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
info.to_fde(Address::Constant(4321))
}
_ => panic!("expected unwind information"),
};
assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 26, lsda: None, instructions: [(4, CfaOffset(224))] }");
}
fn create_multi_return_function(
call_conv: CallConv,
stack_slot: Option<StackSlotData>,
) -> Function {
let mut sig = Signature::new(call_conv);
sig.params.push(AbiParam::new(types::I32));
let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig);
let block0 = func.dfg.make_block();
let v0 = func.dfg.append_block_param(block0, types::I32);
let block1 = func.dfg.make_block();
let block2 = func.dfg.make_block();
let mut pos = FuncCursor::new(&mut func);
pos.insert_block(block0);
pos.ins().brnz(v0, block2, &[]);
pos.ins().jump(block1, &[]);
pos.insert_block(block1);
pos.ins().return_(&[]);
pos.insert_block(block2);
pos.ins().return_(&[]);
if let Some(stack_slot) = stack_slot {
func.stack_slots.push(stack_slot);
}
func
}
}

2839
cranelift/codegen/src/isa/s390x/lower.rs

File diff suppressed because it is too large

296
cranelift/codegen/src/isa/s390x/mod.rs

@ -0,0 +1,296 @@
//! IBM Z 64-bit Instruction Set Architecture.
use crate::ir::condcodes::IntCC;
use crate::ir::Function;
use crate::isa::s390x::settings as s390x_settings;
use crate::isa::unwind::systemv::RegisterMappingError;
use crate::isa::Builder as IsaBuilder;
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
use crate::result::CodegenResult;
use crate::settings as shared_settings;
use alloc::{boxed::Box, vec::Vec};
use core::hash::{Hash, Hasher};
use regalloc::{PrettyPrint, RealRegUniverse, Reg};
use target_lexicon::{Architecture, Triple};
// New backend:
mod abi;
pub(crate) mod inst;
mod lower;
mod settings;
use inst::create_reg_universe;
use self::inst::EmitInfo;
/// A IBM Z backend.
pub struct S390xBackend {
triple: Triple,
flags: shared_settings::Flags,
isa_flags: s390x_settings::Flags,
reg_universe: RealRegUniverse,
}
impl S390xBackend {
/// Create a new IBM Z backend with the given (shared) flags.
pub fn new_with_flags(
triple: Triple,
flags: shared_settings::Flags,
isa_flags: s390x_settings::Flags,
) -> S390xBackend {
let reg_universe = create_reg_universe(&flags);
S390xBackend {
triple,
flags,
isa_flags,
reg_universe,
}
}
/// This performs lowering to VCode, register-allocates the code, computes block layout and
/// finalizes branches. The result is ready for binary emission.
fn compile_vcode(
&self,
func: &Function,
flags: shared_settings::Flags,
) -> CodegenResult<VCode<inst::Inst>> {
let emit_info = EmitInfo::new(flags.clone());
let abi = Box::new(abi::S390xABICallee::new(func, flags)?);
compile::compile::<S390xBackend>(func, self, abi, emit_info)
}
}
impl MachBackend for S390xBackend {
fn compile_function(
&self,
func: &Function,
want_disasm: bool,
) -> CodegenResult<MachCompileResult> {
let flags = self.flags();
let vcode = self.compile_vcode(func, flags.clone())?;
let buffer = vcode.emit();
let frame_size = vcode.frame_size();
let value_labels_ranges = vcode.value_labels_ranges();
let stackslot_offsets = vcode.stackslot_offsets().clone();
let disasm = if want_disasm {
Some(vcode.show_rru(Some(&create_reg_universe(flags))))
} else {
None
};
let buffer = buffer.finish();
Ok(MachCompileResult {
buffer,
frame_size,
disasm,
value_labels_ranges,
stackslot_offsets,
})
}
fn name(&self) -> &'static str {
"s390x"
}
fn triple(&self) -> Triple {
self.triple.clone()
}
fn flags(&self) -> &shared_settings::Flags {
&self.flags
}
fn isa_flags(&self) -> Vec<shared_settings::Value> {
self.isa_flags.iter().collect()
}
fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
self.flags.hash(&mut hasher);
self.isa_flags.hash(&mut hasher);
}
fn reg_universe(&self) -> &RealRegUniverse {
&self.reg_universe
}
fn unsigned_add_overflow_condition(&self) -> IntCC {
unimplemented!()
}
fn unsigned_sub_overflow_condition(&self) -> IntCC {
unimplemented!()
}
#[cfg(feature = "unwind")]
fn emit_unwind_info(
&self,
result: &MachCompileResult,
kind: crate::machinst::UnwindInfoKind,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
use crate::isa::unwind::UnwindInfo;
use crate::machinst::UnwindInfoKind;
Ok(match kind {
UnwindInfoKind::SystemV => {
let mapper = self::inst::unwind::systemv::RegisterMapper;
Some(UnwindInfo::SystemV(
crate::isa::unwind::systemv::create_unwind_info_from_insts(
&result.buffer.unwind_info[..],
result.buffer.data.len(),
&mapper,
)?,
))
}
_ => None,
})
}
#[cfg(feature = "unwind")]
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
Some(inst::unwind::systemv::create_cie())
}
#[cfg(feature = "unwind")]
fn map_reg_to_dwarf(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
inst::unwind::systemv::map_reg(reg).map(|reg| reg.0)
}
}
/// Create a new `isa::Builder`.
pub fn isa_builder(triple: Triple) -> IsaBuilder {
assert!(triple.architecture == Architecture::S390x);
IsaBuilder {
triple,
setup: s390x_settings::builder(),
constructor: |triple, shared_flags, builder| {
let isa_flags = s390x_settings::Flags::new(&shared_flags, builder);
let backend = S390xBackend::new_with_flags(triple, shared_flags, isa_flags);
Box::new(TargetIsaAdapter::new(backend))
},
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::cursor::{Cursor, FuncCursor};
use crate::ir::types::*;
use crate::ir::{AbiParam, ExternalName, Function, InstBuilder, Signature};
use crate::isa::CallConv;
use crate::settings;
use crate::settings::Configurable;
use core::str::FromStr;
use target_lexicon::Triple;
#[test]
fn test_compile_function() {
let name = ExternalName::testcase("test0");
let mut sig = Signature::new(CallConv::SystemV);
sig.params.push(AbiParam::new(I32));
sig.returns.push(AbiParam::new(I32));
let mut func = Function::with_name_signature(name, sig);
let bb0 = func.dfg.make_block();
let arg0 = func.dfg.append_block_param(bb0, I32);
let mut pos = FuncCursor::new(&mut func);
pos.insert_block(bb0);
let v0 = pos.ins().iconst(I32, 0x1234);
let v1 = pos.ins().iadd(arg0, v0);
pos.ins().return_(&[v1]);
let mut shared_flags_builder = settings::builder();
shared_flags_builder.set("opt_level", "none").unwrap();
let shared_flags = settings::Flags::new(shared_flags_builder);
let isa_flags = s390x_settings::Flags::new(&shared_flags, s390x_settings::builder());
let backend = S390xBackend::new_with_flags(
Triple::from_str("s390x").unwrap(),
shared_flags,
isa_flags,
);
let result = backend
.compile_function(&mut func, /* want_disasm = */ false)
.unwrap();
let code = &result.buffer.data[..];
// ahi %r2, 0x1234
// br %r14
let golden = vec![0xa7, 0x2a, 0x12, 0x34, 0x07, 0xfe];
assert_eq!(code, &golden[..]);
}
#[test]
fn test_branch_lowering() {
let name = ExternalName::testcase("test0");
let mut sig = Signature::new(CallConv::SystemV);
sig.params.push(AbiParam::new(I32));
sig.returns.push(AbiParam::new(I32));
let mut func = Function::with_name_signature(name, sig);
let bb0 = func.dfg.make_block();
let arg0 = func.dfg.append_block_param(bb0, I32);
let bb1 = func.dfg.make_block();
let bb2 = func.dfg.make_block();
let bb3 = func.dfg.make_block();
let mut pos = FuncCursor::new(&mut func);
pos.insert_block(bb0);
let v0 = pos.ins().iconst(I32, 0x1234);
let v1 = pos.ins().iadd(arg0, v0);
pos.ins().brnz(v1, bb1, &[]);
pos.ins().jump(bb2, &[]);
pos.insert_block(bb1);
pos.ins().brnz(v1, bb2, &[]);
pos.ins().jump(bb3, &[]);
pos.insert_block(bb2);
let v2 = pos.ins().iadd(v1, v0);
pos.ins().brnz(v2, bb2, &[]);
pos.ins().jump(bb1, &[]);
pos.insert_block(bb3);
let v3 = pos.ins().isub(v1, v0);
pos.ins().return_(&[v3]);
let mut shared_flags_builder = settings::builder();
shared_flags_builder.set("opt_level", "none").unwrap();
let shared_flags = settings::Flags::new(shared_flags_builder);
let isa_flags = s390x_settings::Flags::new(&shared_flags, s390x_settings::builder());
let backend = S390xBackend::new_with_flags(
Triple::from_str("s390x").unwrap(),
shared_flags,
isa_flags,
);
let result = backend
.compile_function(&mut func, /* want_disasm = */ false)
.unwrap();
let code = &result.buffer.data[..];
// FIXME: the branching logic should be optimized more
// ahi %r2, 4660
// chi %r2, 0
// jglh label1 ; jg label2
// jg label6
// jg label3
// ahik %r3, %r2, 4660
// chi %r3, 0
// jglh label4 ; jg label5
// jg label3
// jg label6
// chi %r2, 0
// jglh label7 ; jg label8
// jg label3
// ahi %r2, -4660
// br %r14
let golden = vec![
167, 42, 18, 52, 167, 46, 0, 0, 192, 100, 0, 0, 0, 11, 236, 50, 18, 52, 0, 216, 167,
62, 0, 0, 192, 100, 255, 255, 255, 251, 167, 46, 0, 0, 192, 100, 255, 255, 255, 246,
167, 42, 237, 204, 7, 254,
];
assert_eq!(code, &golden[..]);
}
}

9
cranelift/codegen/src/isa/s390x/settings.rs

@ -0,0 +1,9 @@
//! S390X Settings.
use crate::settings::{self, detail, Builder, Value};
use core::fmt;
// Include code generated by `cranelift-codegen/meta/src/gen_settings.rs:`. This file contains a
// public `Flags` struct with an impl for all of the settings defined in
// `cranelift-codegen/meta/src/isa/s390x/settings.rs`.
include!(concat!(env!("OUT_DIR"), "/settings-s390x.rs"));

1136
cranelift/filetests/filetests/isa/s390x/arithmetic.clif

File diff suppressed because it is too large

243
cranelift/filetests/filetests/isa/s390x/bitops.clif

@ -0,0 +1,243 @@
test compile
target s390x
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BITREV
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; FIXME: bitrev not yet implemented
;function %bitrev_i64(i64) -> i64 {
;block0(v0: i64):
; v1 = bitrev v0
; return v1
;}
;
;function %bitrev_i32(i32) -> i32 {
;block0(v0: i32):
; v1 = bitrev v0
; return v1
;}
;
;function %bitrev_i16(i16) -> i16 {
;block0(v0: i16):
; v1 = bitrev v0
; return v1
;}
;
;function %bitrev_i8(i8) -> i8 {
;block0(v0: i8):
; v1 = bitrev v0
; return v1
;}
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CLZ
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %clz_i64(i64) -> i64 {
block0(v0: i64):
v1 = clz v0
return v1
}
; check: flogr %r0, %r2
; nextln: lgr %r2, %r0
; nextln: br %r14
function %clz_i32(i32) -> i32 {
block0(v0: i32):
v1 = clz v0
return v1
}
; check: llgfr %r2, %r2
; nextln: flogr %r0, %r2
; nextln: lr %r2, %r0
; nextln: ahi %r2, -32
; nextln: br %r14
function %clz_i16(i16) -> i16 {
block0(v0: i16):
v1 = clz v0
return v1
}
; check: llghr %r2, %r2
; nextln: flogr %r0, %r2
; nextln: lr %r2, %r0
; nextln: ahi %r2, -48
; nextln: br %r14
function %clz_i8(i8) -> i8 {
block0(v0: i8):
v1 = clz v0
return v1
}
; check: llgcr %r2, %r2
; nextln: flogr %r0, %r2
; nextln: lr %r2, %r0
; nextln: ahi %r2, -56
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CLS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %cls_i64(i64) -> i64 {
block0(v0: i64):
v1 = cls v0
return v1
}
; check: srag %r3, %r2, 63
; nextln: xgr %r3, %r2
; nextln: flogr %r0, %r2
; nextln: lgr %r2, %r0
; nextln: br %r14
function %cls_i32(i32) -> i32 {
block0(v0: i32):
v1 = cls v0
return v1
}
; check: lgfr %r2, %r2
; nextln: srag %r3, %r2, 63
; nextln: xgr %r3, %r2
; nextln: flogr %r0, %r2
; nextln: lr %r2, %r0
; nextln: ahi %r2, -32
; nextln: br %r14
function %cls_i16(i16) -> i16 {
block0(v0: i16):
v1 = cls v0
return v1
}
; check: lghr %r2, %r2
; nextln: srag %r3, %r2, 63
; nextln: xgr %r3, %r2
; nextln: flogr %r0, %r2
; nextln: lr %r2, %r0
; nextln: ahi %r2, -48
; nextln: br %r14
function %cls_i8(i8) -> i8 {
block0(v0: i8):
v1 = cls v0
return v1
}
; check: lgbr %r2, %r2
; nextln: srag %r3, %r2, 63
; nextln: xgr %r3, %r2
; nextln: flogr %r0, %r2
; nextln: lr %r2, %r0
; nextln: ahi %r2, -56
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CTZ
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %ctz_i64(i64) -> i64 {
block0(v0: i64):
v1 = ctz v0
return v1
}
; check: lcgr %r3, %r2
; nextln: ngrk %r2, %r3, %r2
; nextln: flogr %r0, %r2
; nextln: locghie %r0, -1
; nextln: lghi %r2, 63
; nextln: sgr %r2, %r0
; nextln: br %r14
function %ctz_i32(i32) -> i32 {
block0(v0: i32):
v1 = ctz v0
return v1
}
; check: oihl %r2, 1
; nextln: lcgr %r3, %r2
; nextln: ngrk %r2, %r3, %r2
; nextln: flogr %r0, %r2
; nextln: lhi %r2, 63
; nextln: sr %r2, %r0
; nextln: br %r14
function %ctz_i16(i16) -> i16 {
block0(v0: i16):
v1 = ctz v0
return v1
}
; check: oilh %r2, 1
; nextln: lcgr %r3, %r2
; nextln: ngrk %r2, %r3, %r2
; nextln: flogr %r0, %r2
; nextln: lhi %r2, 63
; nextln: sr %r2, %r0
; nextln: br %r14
function %ctz_i8(i8) -> i8 {
block0(v0: i8):
v1 = ctz v0
return v1
}
; check: oill %r2, 256
; nextln: lcgr %r3, %r2
; nextln: ngrk %r2, %r3, %r2
; nextln: flogr %r0, %r2
; nextln: lhi %r2, 63
; nextln: sr %r2, %r0
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; POPCNT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %popcnt_i64(i64) -> i64 {
block0(v0: i64):
v1 = popcnt v0
return v1
}
; check: popcnt %r2, %r2, 8
; nextln: br %r14
function %popcnt_i32(i32) -> i32 {
block0(v0: i32):
v1 = popcnt v0
return v1
}
; check: llgfr %r2, %r2
; nextln: popcnt %r2, %r2, 8
; nextln: br %r14
function %popcnt_i16(i16) -> i16 {
block0(v0: i16):
v1 = popcnt v0
return v1
}
; check: llghr %r2, %r2
; nextln: popcnt %r2, %r2, 8
; nextln: br %r14
function %popcnt_i8(i8) -> i8 {
block0(v0: i8):
v1 = popcnt v0
return v1
}
; check: popcnt %r2, %r2
; nextln: br %r14

490
cranelift/filetests/filetests/isa/s390x/bitwise.clif

@ -0,0 +1,490 @@
test compile
target s390x
; FIXME: add immediate operand versions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BAND
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %band_i64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = band.i64 v0, v1
return v2
}
; check: ngr %r2, %r3
; nextln: br %r14
function %band_i64_mem(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = load.i64 v1
v3 = band.i64 v0, v2
return v3
}
; check: ng %r2, 0(%r3)
; nextln: br %r14
function %band_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = band.i32 v0, v1
return v2
}
; check: nr %r2, %r3
; nextln: br %r14
function %band_i32_mem(i32, i64) -> i32 {
block0(v0: i32, v1: i64):
v2 = load.i32 v1
v3 = band.i32 v0, v2
return v3
}
; check: n %r2, 0(%r3)
; nextln: br %r14
function %band_i32_memoff(i32, i64) -> i32 {
block0(v0: i32, v1: i64):
v2 = load.i32 v1+4096
v3 = band.i32 v0, v2
return v3
}
; check: ny %r2, 4096(%r3)
; nextln: br %r14
function %band_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = band.i16 v0, v1
return v2
}
; check: nr %r2, %r3
; nextln: br %r14
function %band_i16_mem(i16, i64) -> i16 {
block0(v0: i16, v1: i64):
v2 = load.i16 v1
v3 = band.i16 v0, v2
return v3
}
; check: llh %r3, 0(%r3)
; nextln: nr %r2, %r3
; nextln: br %r14
function %band_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = band.i8 v0, v1
return v2
}
; check: nr %r2, %r3
; nextln: br %r14
function %band_i8_mem(i8, i64) -> i8 {
block0(v0: i8, v1: i64):
v2 = load.i8 v1
v3 = band.i8 v0, v2
return v3
}
; check: llc %r3, 0(%r3)
; nextln: nr %r2, %r3
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BOR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %bor_i64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = bor.i64 v0, v1
return v2
}
; check: ogr %r2, %r3
; nextln: br %r14
function %bor_i64_mem(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = load.i64 v1
v3 = bor.i64 v0, v2
return v3
}
; check: og %r2, 0(%r3)
; nextln: br %r14
function %bor_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bor.i32 v0, v1
return v2
}
; check: or %r2, %r3
; nextln: br %r14
function %bor_i32_mem(i32, i64) -> i32 {
block0(v0: i32, v1: i64):
v2 = load.i32 v1
v3 = bor.i32 v0, v2
return v3
}
; check: o %r2, 0(%r3)
; nextln: br %r14
function %bor_i32_memoff(i32, i64) -> i32 {
block0(v0: i32, v1: i64):
v2 = load.i32 v1+4096
v3 = bor.i32 v0, v2
return v3
}
; check: oy %r2, 4096(%r3)
; nextln: br %r14
function %bor_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = bor.i16 v0, v1
return v2
}
; check: or %r2, %r3
; nextln: br %r14
function %bor_i16_mem(i16, i64) -> i16 {
block0(v0: i16, v1: i64):
v2 = load.i16 v1
v3 = bor.i16 v0, v2
return v3
}
; check: llh %r3, 0(%r3)
; nextln: or %r2, %r3
; nextln: br %r14
function %bor_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = bor.i8 v0, v1
return v2
}
; check: or %r2, %r3
; nextln: br %r14
function %bor_i8_mem(i8, i64) -> i8 {
block0(v0: i8, v1: i64):
v2 = load.i8 v1
v3 = bor.i8 v0, v2
return v3
}
; check: llc %r3, 0(%r3)
; nextln: or %r2, %r3
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BXOR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %bxor_i64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = bxor.i64 v0, v1
return v2
}
; check: xgr %r2, %r3
; nextln: br %r14
function %bxor_i64_mem(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = load.i64 v1
v3 = bxor.i64 v0, v2
return v3
}
; check: xg %r2, 0(%r3)
; nextln: br %r14
function %bxor_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bxor.i32 v0, v1
return v2
}
; check: xr %r2, %r3
; nextln: br %r14
function %bxor_i32_mem(i32, i64) -> i32 {
block0(v0: i32, v1: i64):
v2 = load.i32 v1
v3 = bxor.i32 v0, v2
return v3
}
; check: x %r2, 0(%r3)
; nextln: br %r14
function %bxor_i32_memoff(i32, i64) -> i32 {
block0(v0: i32, v1: i64):
v2 = load.i32 v1+4096
v3 = bxor.i32 v0, v2
return v3
}
; check: xy %r2, 4096(%r3)
; nextln: br %r14
function %bxor_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = bxor.i16 v0, v1
return v2
}
; check: xr %r2, %r3
; nextln: br %r14
function %bxor_i16_mem(i16, i64) -> i16 {
block0(v0: i16, v1: i64):
v2 = load.i16 v1
v3 = bxor.i16 v0, v2
return v3
}
; check: llh %r3, 0(%r3)
; nextln: xr %r2, %r3
; nextln: br %r14
function %bxor_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = bxor.i8 v0, v1
return v2
}
; check: xr %r2, %r3
; nextln: br %r14
function %bxor_i8_mem(i8, i64) -> i8 {
block0(v0: i8, v1: i64):
v2 = load.i8 v1
v3 = bxor.i8 v0, v2
return v3
}
; check: llc %r3, 0(%r3)
; nextln: xr %r2, %r3
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BAND_NOT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %band_not_i64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = band_not.i64 v0, v1
return v2
}
; check: nngrk %r2, %r2, %r3
; nextln: br %r14
function %band_not_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = band_not.i32 v0, v1
return v2
}
; check: nnrk %r2, %r2, %r3
; nextln: br %r14
function %band_not_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = band_not.i16 v0, v1
return v2
}
; check: nnrk %r2, %r2, %r3
; nextln: br %r14
function %band_not_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = band_not.i8 v0, v1
return v2
}
; check: nnrk %r2, %r2, %r3
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BOR_NOT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %bor_not_i64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = bor_not.i64 v0, v1
return v2
}
; check: nogrk %r2, %r2, %r3
; nextln: br %r14
function %bor_not_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bor_not.i32 v0, v1
return v2
}
; check: nork %r2, %r2, %r3
; nextln: br %r14
function %bor_not_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = bor_not.i16 v0, v1
return v2
}
; check: nork %r2, %r2, %r3
; nextln: br %r14
function %bor_not_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = bor_not.i8 v0, v1
return v2
}
; check: nork %r2, %r2, %r3
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BXOR_NOT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %bxor_not_i64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = bxor_not.i64 v0, v1
return v2
}
; check: nxgrk %r2, %r2, %r3
; nextln: br %r14
function %bxor_not_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = bxor_not.i32 v0, v1
return v2
}
; check: nxrk %r2, %r2, %r3
; nextln: br %r14
function %bxor_not_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = bxor_not.i16 v0, v1
return v2
}
; check: nxrk %r2, %r2, %r3
; nextln: br %r14
function %bxor_not_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = bxor_not.i8 v0, v1
return v2
}
; check: nxrk %r2, %r2, %r3
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BNOT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %bnot_i64(i64) -> i64 {
block0(v0: i64):
v1 = bnot.i64 v0
return v1
}
; check: nogrk %r2, %r2, %r2
; nextln: br %r14
function %bnot_i32(i32) -> i32 {
block0(v0: i32):
v1 = bnot.i32 v0
return v1
}
; check: nork %r2, %r2, %r2
; nextln: br %r14
function %bnot_i16(i16) -> i16 {
block0(v0: i16):
v1 = bnot.i16 v0
return v1
}
; check: nork %r2, %r2, %r2
; nextln: br %r14
function %bnot_i8(i8) -> i8 {
block0(v0: i8):
v1 = bnot.i8 v0
return v1
}
; check: nork %r2, %r2, %r2
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BITSELECT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %bitselect_i64(i64, i64, i64) -> i64 {
block0(v0: i64, v1: i64, v2: i64):
v3 = bitselect.i64 v0, v1, v2
return v3
}
; check: ngr %r3, %r2
; nextln: nngrk %r2, %r4, %r2
; nextln: ogr %r2, %r3
; nextln: br %r14
function %bitselect_i32(i32, i32, i32) -> i32 {
block0(v0: i32, v1: i32, v2: i32):
v3 = bitselect.i32 v0, v1, v2
return v3
}
; check: nr %r3, %r2
; nextln: nnrk %r2, %r4, %r2
; nextln: or %r2, %r3
; nextln: br %r14
function %bitselect_i16(i16, i16, i16) -> i16 {
block0(v0: i16, v1: i16, v2: i16):
v3 = bitselect.i16 v0, v1, v2
return v3
}
; check: nr %r3, %r2
; nextln: nnrk %r2, %r4, %r2
; nextln: or %r2, %r3
; nextln: br %r14
function %bitselect_i8(i8, i8, i8) -> i8 {
block0(v0: i8, v1: i8, v2: i8):
v3 = bitselect.i8 v0, v1, v2
return v3
}
; check: nr %r3, %r2
; nextln: nnrk %r2, %r4, %r2
; nextln: or %r2, %r3
; nextln: br %r14

113
cranelift/filetests/filetests/isa/s390x/call.clif

@ -0,0 +1,113 @@
test compile
target s390x
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CALL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %call(i64) -> i64 {
fn0 = %g(i64) -> i64
block0(v0: i64):
v1 = call fn0(v0)
return v1
}
; check: stmg %r14, %r15, 112(%r15)
; nextln: aghi %r15, -160
; nextln: virtual_sp_offset_adjust 160
; nextln: bras %r1, 12 ; data %g + 0 ; lg %r3, 0(%r1)
; nextln: basr %r14, %r3
; nextln: lmg %r14, %r15, 272(%r15)
; nextln: br %r14
function %call_uext(i32) -> i64 {
fn0 = %g(i32 uext) -> i64
block0(v0: i32):
v1 = call fn0(v0)
return v1
}
; check: stmg %r14, %r15, 112(%r15)
; nextln: aghi %r15, -160
; nextln: virtual_sp_offset_adjust 160
; nextln: llgfr %r2, %r2
; nextln: bras %r1, 12 ; data %g + 0 ; lg %r3, 0(%r1)
; nextln: basr %r14, %r3
; nextln: lmg %r14, %r15, 272(%r15)
; nextln: br %r14
function %ret_uext(i32) -> i32 uext {
block0(v0: i32):
return v0
}
; check: llgfr %r2, %r2
; nextln: br %r14
function %call_uext(i32) -> i64 {
fn0 = %g(i32 sext) -> i64
block0(v0: i32):
v1 = call fn0(v0)
return v1
}
; check: stmg %r14, %r15, 112(%r15)
; nextln: aghi %r15, -160
; nextln: virtual_sp_offset_adjust 160
; nextln: lgfr %r2, %r2
; nextln: bras %r1, 12 ; data %g + 0 ; lg %r3, 0(%r1)
; nextln: basr %r14, %r3
; nextln: lmg %r14, %r15, 272(%r15)
; nextln: br %r14
function %ret_uext(i32) -> i32 sext {
block0(v0: i32):
return v0
}
; check: lgfr %r2, %r2
; nextln: br %r14
function %call_colocated(i64) -> i64 {
fn0 = colocated %g(i64) -> i64
block0(v0: i64):
v1 = call fn0(v0)
return v1
}
; check: stmg %r14, %r15, 112(%r15)
; nextln: aghi %r15, -160
; nextln: virtual_sp_offset_adjust 160
; nextln: brasl %r14, %g
; nextln: lmg %r14, %r15, 272(%r15)
; nextln: br %r14
function %f2(i32) -> i64 {
fn0 = %g(i32 uext) -> i64
block0(v0: i32):
v1 = call fn0(v0)
return v1
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CALL_INDIRECT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %call_indirect(i64, i64) -> i64 {
sig0 = (i64) -> i64
block0(v0: i64, v1: i64):
v2 = call_indirect.i64 sig0, v1(v0)
return v2
}
; check: stmg %r14, %r15, 112(%r15)
; nextln: aghi %r15, -160
; nextln: virtual_sp_offset_adjust 160
; nextln: basr %r14, %r3
; nextln: lmg %r14, %r15, 272(%r15)
; nextln: br %r14

62
cranelift/filetests/filetests/isa/s390x/condbr.clif

@ -0,0 +1,62 @@
test compile
target s390x
function %f(i64, i64) -> b1 {
block0(v0: i64, v1: i64):
v2 = icmp eq v0, v1
return v2
}
; check: clgr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochie %r2, 1
; nextln: br %r14
function %f(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = icmp eq v0, v1
brnz v2, block1
jump block2
block1:
v4 = iconst.i64 1
return v4
block2:
v5 = iconst.i64 2
return v5
}
; check: Block 0:
; check: clgr %r2, %r3
; nextln: jge label1 ; jg label2
; check: Block 1:
; check: lghi %r2, 1
; nextln: br %r14
; check: Block 2:
; check: lghi %r2, 2
; nextln: br %r14
function %f(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = icmp eq v0, v1
brnz v2, block1
jump block1
block1:
v4 = iconst.i64 1
return v4
}
; FIXME: Should optimize away branches
; check: Block 0:
; check: clgr %r2, %r3
; nextln: jge label1 ; jg label2
; check: Block 1:
; check: jg label3
; check: Block 2:
; check: jg label3
; check: Block 3:
; check: lghi %r2, 1
; nextln: br %r14

43
cranelift/filetests/filetests/isa/s390x/condops.clif

@ -0,0 +1,43 @@
test compile
target s390x
function %f(i8, i64, i64) -> i64 {
block0(v0: i8, v1: i64, v2: i64):
v3 = iconst.i8 42
v4 = icmp eq v0, v3
v5 = select.i64 v4, v1, v2
return v5
}
; check: llcr %r2, %r2
; nextln: clfi %r2, 42
; nextln: locgre %r4, %r3
; nextln: lgr %r2, %r4
; nextln: br %r14
function %g(b1, i8, i8) -> i8 {
block0(v0: b1, v1: i8, v2: i8):
v3 = select.i8 v0, v1, v2
return v3
}
; FIXME: optimize i8/i16 compares
; check: llcr %r2, %r2
; nextln: chi %r2, 0
; nextln: locrlh %r4, %r3
; nextln: lr %r2, %r4
; nextln: br %r14
function %i(i32, i8, i8) -> i8 {
block0(v0: i32, v1: i8, v2: i8):
v3 = iconst.i32 42
v4 = icmp.i32 eq v0, v3
v5 = select.i8 v4, v1, v2
return v5
}
; check: clfi %r2, 42
; nextln: locre %r4, %r3
; nextln: lr %r2, %r4
; nextln: br %r14

113
cranelift/filetests/filetests/isa/s390x/constants.clif

@ -0,0 +1,113 @@
test compile
target s390x
function %f() -> b8 {
block0:
v0 = bconst.b8 true
return v0
}
; check: lhi %r2, 255
; nextln: br %r14
function %f() -> b16 {
block0:
v0 = bconst.b16 false
return v0
}
; check: lhi %r2, 0
; nextln: br %r14
function %f() -> i64 {
block0:
v0 = iconst.i64 0
return v0
}
; check: lghi %r2, 0
; nextln: br %r14
function %f() -> i64 {
block0:
v0 = iconst.i64 0xffff
return v0
}
; check: lgfi %r2, 65535
; nextln: br %r14
function %f() -> i64 {
block0:
v0 = iconst.i64 0xffff0000
return v0
}
; check: llilh %r2, 65535
; nextln: br %r14
function %f() -> i64 {
block0:
v0 = iconst.i64 0xffff00000000
return v0
}
; check: llihl %r2, 65535
; nextln: br %r14
function %f() -> i64 {
block0:
v0 = iconst.i64 0xffff000000000000
return v0
}
; check: llihh %r2, 65535
; nextln: br %r14
function %f() -> i64 {
block0:
v0 = iconst.i64 0xffffffffffffffff
return v0
}
; check: lghi %r2, -1
; nextln: br %r14
function %f() -> i64 {
block0:
v0 = iconst.i64 0xffffffffffff0000
return v0
}
; check: lgfi %r2, -65536
; nextln: br %r14
function %f() -> i64 {
block0:
v0 = iconst.i64 0xf34bf0a31212003a ; random digits
return v0
}
; check: llihf %r2, 4081840291
; nextln: iilf %r2, 303169594
; nextln: br %r14
function %f() -> i64 {
block0:
v0 = iconst.i64 0x12e900001ef40000 ; random digits with 2 clear half words
return v0
}
; check: llihh %r2, 4841
; nextln: iilh %r2, 7924
; nextln: br %r14
function %f() -> i32 {
block0:
v0 = iconst.i32 -1
return v0
}
; check: lhi %r2, -1
; nextln: br %r14

748
cranelift/filetests/filetests/isa/s390x/conversions.clif

@ -0,0 +1,748 @@
test compile
target s390x
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; UEXTEND
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %uextend_i32_i64(i32) -> i64 {
block0(v0: i32):
v1 = uextend.i64 v0
return v1
}
; check: llgfr %r2, %r2
; nextln: br %r14
function %uextend_i16_i64(i16) -> i64 {
block0(v0: i16):
v1 = uextend.i64 v0
return v1
}
; check: llghr %r2, %r2
; nextln: br %r14
function %uextend_i16_i32(i16) -> i32 {
block0(v0: i16):
v1 = uextend.i32 v0
return v1
}
; check: llhr %r2, %r2
; nextln: br %r14
function %uextend_i8_i64(i8) -> i64 {
block0(v0: i8):
v1 = uextend.i64 v0
return v1
}
; check: llgcr %r2, %r2
; nextln: br %r14
function %uextend_i8_i32(i8) -> i32 {
block0(v0: i8):
v1 = uextend.i32 v0
return v1
}
; check: llcr %r2, %r2
; nextln: br %r14
function %uextend_i8_i16(i8) -> i16 {
block0(v0: i8):
v1 = uextend.i16 v0
return v1
}
; check: llcr %r2, %r2
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SEXTEND
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %sextend_i32_i64(i32) -> i64 {
block0(v0: i32):
v1 = sextend.i64 v0
return v1
}
; check: lgfr %r2, %r2
; nextln: br %r14
function %sextend_i16_i64(i16) -> i64 {
block0(v0: i16):
v1 = sextend.i64 v0
return v1
}
; check: lghr %r2, %r2
; nextln: br %r14
function %sextend_i16_i32(i16) -> i32 {
block0(v0: i16):
v1 = sextend.i32 v0
return v1
}
; check: lhr %r2, %r2
; nextln: br %r14
function %sextend_i8_i64(i8) -> i64 {
block0(v0: i8):
v1 = sextend.i64 v0
return v1
}
; check: lgbr %r2, %r2
; nextln: br %r14
function %sextend_i8_i32(i8) -> i32 {
block0(v0: i8):
v1 = sextend.i32 v0
return v1
}
; check: lbr %r2, %r2
; nextln: br %r14
function %sextend_i8_i16(i8) -> i16 {
block0(v0: i8):
v1 = sextend.i16 v0
return v1
}
; check: lbr %r2, %r2
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; IREDUCE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %ireduce_i64_i32(i64, i64) -> i32 {
block0(v0: i64, v1: i64):
v2 = ireduce.i32 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %ireduce_i64_i16(i64, i64) -> i16 {
block0(v0: i64, v1: i64):
v2 = ireduce.i16 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %ireduce_i64_i8(i64, i64) -> i8 {
block0(v0: i64, v1: i64):
v2 = ireduce.i8 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %ireduce_i32_i16(i32, i32) -> i16 {
block0(v0: i32, v1: i32):
v2 = ireduce.i16 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %ireduce_i32_i8(i32, i32) -> i8 {
block0(v0: i32, v1: i32):
v2 = ireduce.i8 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %ireduce_i16_i8(i16, i16) -> i8 {
block0(v0: i16, v1: i16):
v2 = ireduce.i8 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BEXTEND
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %bextend_b32_b64(b32) -> b64 {
block0(v0: b32):
v1 = bextend.b64 v0
return v1
}
; check: lgfr %r2, %r2
; nextln: br %r14
function %bextend_b16_b64(b16) -> b64 {
block0(v0: b16):
v1 = bextend.b64 v0
return v1
}
; check: lghr %r2, %r2
; nextln: br %r14
function %bextend_b16_b32(b16) -> b32 {
block0(v0: b16):
v1 = bextend.b32 v0
return v1
}
; check: lhr %r2, %r2
; nextln: br %r14
function %bextend_b8_b64(b8) -> b64 {
block0(v0: b8):
v1 = bextend.b64 v0
return v1
}
; check: lgbr %r2, %r2
; nextln: br %r14
function %bextend_b8_b32(b8) -> b32 {
block0(v0: b8):
v1 = bextend.b32 v0
return v1
}
; check: lbr %r2, %r2
; nextln: br %r14
function %bextend_b8_b16(b8) -> b16 {
block0(v0: b8):
v1 = bextend.b16 v0
return v1
}
; check: lbr %r2, %r2
; nextln: br %r14
function %bextend_b1_b64(b1) -> b64 {
block0(v0: b1):
v1 = bextend.b64 v0
return v1
}
; check: sllg %r2, %r2, 63
; nextln: srag %r2, %r2, 63
; nextln: br %r14
function %bextend_b1_b32(b1) -> b32 {
block0(v0: b1):
v1 = bextend.b32 v0
return v1
}
; check: sllk %r2, %r2, 31
; nextln: srak %r2, %r2, 31
; nextln: br %r14
function %bextend_b1_b16(b1) -> b16 {
block0(v0: b1):
v1 = bextend.b16 v0
return v1
}
; check: sllk %r2, %r2, 31
; nextln: srak %r2, %r2, 31
; nextln: br %r14
function %bextend_b1_b8(b1) -> b8 {
block0(v0: b1):
v1 = bextend.b8 v0
return v1
}
; check: sllk %r2, %r2, 31
; nextln: srak %r2, %r2, 31
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BREDUCE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %breduce_b64_b32(b64, b64) -> b32 {
block0(v0: b64, v1: b64):
v2 = breduce.b32 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %breduce_b64_b16(b64, b64) -> b16 {
block0(v0: b64, v1: b64):
v2 = breduce.b16 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %breduce_b64_b8(b64, b64) -> b8 {
block0(v0: b64, v1: b64):
v2 = breduce.b8 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %breduce_b64_b1(b64, b64) -> b1 {
block0(v0: b64, v1: b64):
v2 = breduce.b1 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %breduce_b32_b16(b32, b32) -> b16 {
block0(v0: b32, v1: b32):
v2 = breduce.b16 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %breduce_b32_b8(b32, b32) -> b8 {
block0(v0: b32, v1: b32):
v2 = breduce.b8 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %breduce_b32_b1(b32, b32) -> b1 {
block0(v0: b32, v1: b32):
v2 = breduce.b1 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %breduce_b16_b8(b16, b16) -> b8 {
block0(v0: b16, v1: b16):
v2 = breduce.b8 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %breduce_b16_b1(b16, b16) -> b1 {
block0(v0: b16, v1: b16):
v2 = breduce.b1 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %breduce_b8_b1(b8, b8) -> b1 {
block0(v0: b8, v1: b8):
v2 = breduce.b1 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BMASK
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %bmask_b64_i64(b64, b64) -> i64 {
block0(v0: b64, v1: b64):
v2 = bmask.i64 v1
return v2
}
; check: lgr %r2, %r3
; nextln: br %r14
function %bmask_b64_i32(b64, b64) -> i32 {
block0(v0: b64, v1: b64):
v2 = bmask.i32 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %bmask_b64_i16(b64, b64) -> i16 {
block0(v0: b64, v1: b64):
v2 = bmask.i16 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %bmask_b64_i8(b64, b64) -> i8 {
block0(v0: b64, v1: b64):
v2 = bmask.i8 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %bmask_b32_i64(b32, b32) -> i64 {
block0(v0: b32, v1: b32):
v2 = bmask.i64 v1
return v2
}
; check: lgfr %r2, %r3
; nextln: br %r14
function %bmask_b32_i32(b32, b32) -> i32 {
block0(v0: b32, v1: b32):
v2 = bmask.i32 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %bmask_b32_i16(b32, b32) -> i16 {
block0(v0: b32, v1: b32):
v2 = bmask.i16 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %bmask_b32_i8(b32, b32) -> i8 {
block0(v0: b32, v1: b32):
v2 = bmask.i8 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %bmask_b16_i64(b16, b16) -> i64 {
block0(v0: b16, v1: b16):
v2 = bmask.i64 v1
return v2
}
; check: lghr %r2, %r3
; nextln: br %r14
function %bmask_b16_i32(b16, b16) -> i32 {
block0(v0: b16, v1: b16):
v2 = bmask.i32 v1
return v2
}
; check: lhr %r2, %r3
; nextln: br %r14
function %bmask_b16_i16(b16, b16) -> i16 {
block0(v0: b16, v1: b16):
v2 = bmask.i16 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %bmask_b16_i8(b16, b16) -> i8 {
block0(v0: b16, v1: b16):
v2 = bmask.i8 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %bmask_b8_i64(b8, b8) -> i64 {
block0(v0: b8, v1: b8):
v2 = bmask.i64 v1
return v2
}
; check: lgbr %r2, %r3
; nextln: br %r14
function %bmask_b8_i32(b8, b8) -> i32 {
block0(v0: b8, v1: b8):
v2 = bmask.i32 v1
return v2
}
; check: lbr %r2, %r3
; nextln: br %r14
function %bmask_b8_i16(b8, b8) -> i16 {
block0(v0: b8, v1: b8):
v2 = bmask.i16 v1
return v2
}
; check: lbr %r2, %r3
; nextln: br %r14
function %bmask_b8_i8(b8, b8) -> i8 {
block0(v0: b8, v1: b8):
v2 = bmask.i8 v1
return v2
}
; check: lr %r2, %r3
; nextln: br %r14
function %bmask_b1_i64(b1, b1) -> i64 {
block0(v0: b1, v1: b1):
v2 = bmask.i64 v1
return v2
}
; check: sllg %r2, %r3, 63
; nextln: srag %r2, %r2, 63
; nextln: br %r14
function %bmask_b1_i32(b1, b1) -> i32 {
block0(v0: b1, v1: b1):
v2 = bmask.i32 v1
return v2
}
; check: sllk %r2, %r3, 31
; nextln: srak %r2, %r2, 31
; nextln: br %r14
function %bmask_b1_i16(b1, b1) -> i16 {
block0(v0: b1, v1: b1):
v2 = bmask.i16 v1
return v2
}
; check: sllk %r2, %r3, 31
; nextln: srak %r2, %r2, 31
; nextln: br %r14
function %bmask_b1_i8(b1, b1) -> i8 {
block0(v0: b1, v1: b1):
v2 = bmask.i8 v1
return v2
}
; check: sllk %r2, %r3, 31
; nextln: srak %r2, %r2, 31
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BINT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %bint_b64_i64(b64) -> i64 {
block0(v0: b64):
v1 = bint.i64 v0
return v1
}
; check: lghi %r3, 1
; nextln: ngr %r2, %r3
; nextln: br %r14
function %bint_b64_i32(b64) -> i32 {
block0(v0: b64):
v1 = bint.i32 v0
return v1
}
; check: nilf %r2, 1
; nextln: br %r14
function %bint_b64_i16(b64) -> i16 {
block0(v0: b64):
v1 = bint.i16 v0
return v1
}
; check: nill %r2, 1
; nextln: br %r14
function %bint_b64_i8(b64) -> i8 {
block0(v0: b64):
v1 = bint.i8 v0
return v1
}
; check: nill %r2, 1
; nextln: br %r14
function %bint_b32_i64(b32) -> i64 {
block0(v0: b32):
v1 = bint.i64 v0
return v1
}
; check: lghi %r3, 1
; nextln: ngr %r2, %r3
; nextln: br %r14
function %bint_b32_i32(b32) -> i32 {
block0(v0: b32):
v1 = bint.i32 v0
return v1
}
; check: nilf %r2, 1
; nextln: br %r14
function %bint_b32_i16(b32) -> i16 {
block0(v0: b32):
v1 = bint.i16 v0
return v1
}
; check: nill %r2, 1
; nextln: br %r14
function %bint_b32_i8(b32) -> i8 {
block0(v0: b32):
v1 = bint.i8 v0
return v1
}
; check: nill %r2, 1
; nextln: br %r14
function %bint_b16_i64(b16) -> i64 {
block0(v0: b16):
v1 = bint.i64 v0
return v1
}
; check: lghi %r3, 1
; nextln: ngr %r2, %r3
; nextln: br %r14
function %bint_b16_i32(b16) -> i32 {
block0(v0: b16):
v1 = bint.i32 v0
return v1
}
; check: nilf %r2, 1
; nextln: br %r14
function %bint_b16_i16(b16) -> i16 {
block0(v0: b16):
v1 = bint.i16 v0
return v1
}
; check: nill %r2, 1
; nextln: br %r14
function %bint_b16_i8(b16) -> i8 {
block0(v0: b16):
v1 = bint.i8 v0
return v1
}
; check: nill %r2, 1
; nextln: br %r14
function %bint_b8_i64(b8) -> i64 {
block0(v0: b8):
v1 = bint.i64 v0
return v1
}
; check: lghi %r3, 1
; nextln: ngr %r2, %r3
; nextln: br %r14
function %bint_b8_i32(b8) -> i32 {
block0(v0: b8):
v1 = bint.i32 v0
return v1
}
; check: nilf %r2, 1
; nextln: br %r14
function %bint_b8_i16(b8) -> i16 {
block0(v0: b8):
v1 = bint.i16 v0
return v1
}
; check: nill %r2, 1
; nextln: br %r14
function %bint_b8_i8(b8) -> i8 {
block0(v0: b8):
v1 = bint.i8 v0
return v1
}
; check: nill %r2, 1
; nextln: br %r14
function %bint_b1_i64(b1) -> i64 {
block0(v0: b1):
v1 = bint.i64 v0
return v1
}
; check: lghi %r3, 1
; nextln: ngr %r2, %r3
; nextln: br %r14
function %bint_b1_i32(b1) -> i32 {
block0(v0: b1):
v1 = bint.i32 v0
return v1
}
; check: nilf %r2, 1
; nextln: br %r14
function %bint_b1_i16(b1) -> i16 {
block0(v0: b1):
v1 = bint.i16 v0
return v1
}
; check: nill %r2, 1
; nextln: br %r14
function %bint_b1_i8(b1) -> i8 {
block0(v0: b1):
v1 = bint.i8 v0
return v1
}
; check: nill %r2, 1
; nextln: br %r14

355
cranelift/filetests/filetests/isa/s390x/div-traps.clif

@ -0,0 +1,355 @@
test compile
set avoid_div_traps=1
target s390x
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SDIV
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %sdiv_i64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = sdiv.i64 v0, v1
return v2
}
; check: lgr %r1, %r2
; nextln: cgite %r3, 0
; nextln: llihf %r2, 2147483647
; nextln: iilf %r2, 4294967295
; nextln: xgr %r2, %r1
; nextln: ngr %r2, %r3
; nextln: cgite %r2, -1
; nextln: dsgr %r0, %r3
; nextln: lgr %r2, %r1
; nextln: br %r14
function %sdiv_i64_imm(i64) -> i64 {
block0(v0: i64):
v1 = iconst.i64 2
v2 = sdiv.i64 v0, v1
return v2
}
; check: lgr %r1, %r2
; nextln: lghi %r2, 2
; nextln: dsgr %r0, %r2
; nextln: lgr %r2, %r1
; nextln: br %r14
function %sdiv_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = sdiv.i32 v0, v1
return v2
}
; check: lgfr %r1, %r2
; nextln: cite %r3, 0
; nextln: iilf %r2, 2147483647
; nextln: xr %r2, %r1
; nextln: nr %r2, %r3
; nextln: cite %r2, -1
; nextln: dsgfr %r0, %r3
; nextln: lr %r2, %r1
; nextln: br %r14
function %sdiv_i32_imm(i32) -> i32 {
block0(v0: i32):
v1 = iconst.i32 2
v2 = sdiv.i32 v0, v1
return v2
}
; check: lgfr %r1, %r2
; nextln: lhi %r2, 2
; nextln: dsgfr %r0, %r2
; nextln: lr %r2, %r1
; nextln: br %r14
function %sdiv_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = sdiv.i16 v0, v1
return v2
}
; check: lghr %r1, %r2
; nextln: lhr %r2, %r3
; nextln: cite %r2, 0
; nextln: lhi %r3, 32767
; nextln: xr %r3, %r1
; nextln: nr %r3, %r2
; nextln: cite %r3, -1
; nextln: dsgfr %r0, %r2
; nextln: lr %r2, %r1
; nextln: br %r14
function %sdiv_i16_imm(i16) -> i16 {
block0(v0: i16):
v1 = iconst.i16 2
v2 = sdiv.i16 v0, v1
return v2
}
; check: lghr %r1, %r2
; nextln: lhi %r2, 2
; nextln: dsgfr %r0, %r2
; nextln: lr %r2, %r1
; nextln: br %r14
function %sdiv_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = sdiv.i8 v0, v1
return v2
}
; check: lgbr %r1, %r2
; nextln: lbr %r2, %r3
; nextln: cite %r2, 0
; nextln: lhi %r3, 127
; nextln: xr %r3, %r1
; nextln: nr %r3, %r2
; nextln: cite %r3, -1
; nextln: dsgfr %r0, %r2
; nextln: lr %r2, %r1
; nextln: br %r14
function %sdiv_i8_imm(i8) -> i8 {
block0(v0: i8):
v1 = iconst.i8 2
v2 = sdiv.i8 v0, v1
return v2
}
; check: lgbr %r1, %r2
; nextln: lhi %r2, 2
; nextln: dsgfr %r0, %r2
; nextln: lr %r2, %r1
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; UDIV
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %udiv_i64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = udiv.i64 v0, v1
return v2
}
; check: lghi %r0, 0
; nextln: lgr %r1, %r2
; nextln: cgite %r3, 0
; nextln: dlgr %r0, %r3
; nextln: lgr %r2, %r1
; nextln: br %r14
function %udiv_i64_imm(i64) -> i64 {
block0(v0: i64):
v1 = iconst.i64 2
v2 = udiv.i64 v0, v1
return v2
}
; check: lghi %r0, 0
; nextln: lgr %r1, %r2
; nextln: lghi %r2, 2
; nextln: dlgr %r0, %r2
; nextln: lgr %r2, %r1
; nextln: br %r14
function %udiv_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = udiv.i32 v0, v1
return v2
}
; check: lhi %r0, 0
; nextln: lr %r1, %r2
; nextln: cite %r3, 0
; nextln: dlr %r0, %r3
; nextln: lr %r2, %r1
; nextln: br %r14
function %udiv_i32_imm(i32) -> i32 {
block0(v0: i32):
v1 = iconst.i32 2
v2 = udiv.i32 v0, v1
return v2
}
; check: lhi %r0, 0
; nextln: lr %r1, %r2
; nextln: lhi %r2, 2
; nextln: dlr %r0, %r2
; nextln: lr %r2, %r1
; nextln: br %r14
function %udiv_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = udiv.i16 v0, v1
return v2
}
; check: lhi %r0, 0
; nextln: llhr %r1, %r2
; nextln: llhr %r2, %r3
; nextln: cite %r2, 0
; nextln: dlr %r0, %r2
; nextln: lr %r2, %r1
; nextln: br %r14
function %udiv_i16_imm(i16) -> i16 {
block0(v0: i16):
v1 = iconst.i16 2
v2 = udiv.i16 v0, v1
return v2
}
; check: lhi %r0, 0
; nextln: llhr %r1, %r2
; nextln: lhi %r2, 2
; nextln: dlr %r0, %r2
; nextln: lr %r2, %r1
; nextln: br %r14
function %udiv_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = udiv.i8 v0, v1
return v2
}
; check: lhi %r0, 0
; nextln: llcr %r1, %r2
; nextln: llcr %r2, %r3
; nextln: cite %r2, 0
; nextln: dlr %r0, %r2
; nextln: lr %r2, %r1
; nextln: br %r14
function %udiv_i8_imm(i8) -> i8 {
block0(v0: i8):
v1 = iconst.i8 2
v2 = udiv.i8 v0, v1
return v2
}
; check: lhi %r0, 0
; nextln: llcr %r1, %r2
; nextln: lhi %r2, 2
; nextln: dlr %r0, %r2
; nextln: lr %r2, %r1
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SREM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %srem_i64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = srem.i64 v0, v1
return v2
}
; check: lgr %r1, %r2
; nextln: cgite %r3, 0
; nextln: cghi %r3, -1
; nextln: locghie %r1, 0
; nextln: dsgr %r0, %r3
; nextln: lgr %r2, %r0
; nextln: br %r14
function %srem_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = srem.i32 v0, v1
return v2
}
; check: lgfr %r1, %r2
; nextln: cite %r3, 0
; nextln: dsgfr %r0, %r3
; nextln: lr %r2, %r0
; nextln: br %r14
function %srem_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = srem.i16 v0, v1
return v2
}
; check: lghr %r1, %r2
; nextln: lhr %r2, %r3
; nextln: cite %r2, 0
; nextln: dsgfr %r0, %r2
; nextln: lr %r2, %r0
; nextln: br %r14
function %srem_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = srem.i8 v0, v1
return v2
}
; check: lgbr %r1, %r2
; nextln: lbr %r2, %r3
; nextln: cite %r2, 0
; nextln: dsgfr %r0, %r2
; nextln: lr %r2, %r0
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; UREM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %urem_i64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = urem.i64 v0, v1
return v2
}
; check: lghi %r0, 0
; nextln: lgr %r1, %r2
; nextln: cgite %r3, 0
; nextln: dlgr %r0, %r3
; nextln: lgr %r2, %r0
; nextln: br %r14
function %urem_i32(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = urem.i32 v0, v1
return v2
}
; check: lhi %r0, 0
; nextln: lr %r1, %r2
; nextln: cite %r3, 0
; nextln: dlr %r0, %r3
; nextln: lr %r2, %r0
; nextln: br %r14
function %urem_i16(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = urem.i16 v0, v1
return v2
}
; check: lhi %r0, 0
; nextln: llhr %r1, %r2
; nextln: llhr %r2, %r3
; nextln: cite %r2, 0
; nextln: dlr %r0, %r2
; nextln: lr %r2, %r0
; nextln: br %r14
function %urem_i8(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = urem.i8 v0, v1
return v2
}
; check: lhi %r0, 0
; nextln: llcr %r1, %r2
; nextln: llcr %r2, %r3
; nextln: cite %r2, 0
; nextln: dlr %r0, %r2
; nextln: lr %r2, %r0
; nextln: br %r14

711
cranelift/filetests/filetests/isa/s390x/floating-point.clif

@ -0,0 +1,711 @@
test compile
target s390x
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; F32CONST/F64CONST
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; FIXME: should use FZERO instruction
; FIXME: should use out-of-line literal pool
function %f32const_zero() -> f32 {
block0:
v1 = f32const 0x0.0
return v1
}
; check: bras %r1, 8 ; data.f32 0 ; le %f0, 0(%r1)
; nextln: br %r14
function %f64const_zero() -> f64 {
block0:
v1 = f64const 0x0.0
return v1
}
; check: bras %r1, 12 ; data.f64 0 ; ld %f0, 0(%r1)
; nextln: br %r14
function %f32const_one() -> f32 {
block0:
v1 = f32const 0x1.0
return v1
}
; check: bras %r1, 8 ; data.f32 1 ; le %f0, 0(%r1)
; nextln: br %r14
function %f64const_one() -> f64 {
block0:
v1 = f64const 0x1.0
return v1
}
; check: bras %r1, 12 ; data.f64 1 ; ld %f0, 0(%r1)
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FADD
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fadd_f32(f32, f32) -> f32 {
block0(v0: f32, v1: f32):
v2 = fadd v0, v1
return v2
}
; check: aebr %f0, %f2
; nextln: br %r14
function %fadd_f64(f64, f64) -> f64 {
block0(v0: f64, v1: f64):
v2 = fadd v0, v1
return v2
}
; check: adbr %f0, %f2
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FSUB
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fsub_f32(f32, f32) -> f32 {
block0(v0: f32, v1: f32):
v2 = fsub v0, v1
return v2
}
; check: sebr %f0, %f2
; nextln: br %r14
function %fsub_f64(f64, f64) -> f64 {
block0(v0: f64, v1: f64):
v2 = fsub v0, v1
return v2
}
; check: sdbr %f0, %f2
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FMUL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fmul_f32(f32, f32) -> f32 {
block0(v0: f32, v1: f32):
v2 = fmul v0, v1
return v2
}
; check: meebr %f0, %f2
; nextln: br %r14
function %fmul_f64(f64, f64) -> f64 {
block0(v0: f64, v1: f64):
v2 = fmul v0, v1
return v2
}
; check: mdbr %f0, %f2
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FDIV
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fdiv_f32(f32, f32) -> f32 {
block0(v0: f32, v1: f32):
v2 = fdiv v0, v1
return v2
}
; check: debr %f0, %f2
; nextln: br %r14
function %fdiv_f64(f64, f64) -> f64 {
block0(v0: f64, v1: f64):
v2 = fdiv v0, v1
return v2
}
; check: ddbr %f0, %f2
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FMIN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fmin_f32(f32, f32) -> f32 {
block0(v0: f32, v1: f32):
v2 = fmin v0, v1
return v2
}
; check: wfminsb %f0, %f0, %f2, 1
; nextln: br %r14
function %fmin_f64(f64, f64) -> f64 {
block0(v0: f64, v1: f64):
v2 = fmin v0, v1
return v2
}
; check: wfmindb %f0, %f0, %f2, 1
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FMAX
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fmax_f32(f32, f32) -> f32 {
block0(v0: f32, v1: f32):
v2 = fmax v0, v1
return v2
}
; check: wfmaxsb %f0, %f0, %f2, 1
; nextln: br %r14
function %fmax_f64(f64, f64) -> f64 {
block0(v0: f64, v1: f64):
v2 = fmax v0, v1
return v2
}
; check: wfmaxdb %f0, %f0, %f2, 1
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SQRT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %sqrt_f32(f32) -> f32 {
block0(v0: f32):
v1 = sqrt v0
return v1
}
; check: sqebr %f0, %f0
; nextln: br %r14
function %sqrt_f64(f64) -> f64 {
block0(v0: f64):
v1 = sqrt v0
return v1
}
; check: sqdbr %f0, %f0
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FABS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fabs_f32(f32) -> f32 {
block0(v0: f32):
v1 = fabs v0
return v1
}
; check: lpebr %f0, %f0
; nextln: br %r14
function %fabs_f64(f64) -> f64 {
block0(v0: f64):
v1 = fabs v0
return v1
}
; check: lpdbr %f0, %f0
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FNEG
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fneg_f32(f32) -> f32 {
block0(v0: f32):
v1 = fneg v0
return v1
}
; check: lcebr %f0, %f0
; nextln: br %r14
function %fneg_f64(f64) -> f64 {
block0(v0: f64):
v1 = fneg v0
return v1
}
; check: lcdbr %f0, %f0
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FPROMOTE/FDEMOTE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fpromote_f32(f32) -> f64 {
block0(v0: f32):
v1 = fpromote.f64 v0
return v1
}
; check: ldebr %f0, %f0
; nextln: br %r14
function %fdemote_f64(f64) -> f32 {
block0(v0: f64):
v1 = fdemote.f32 v0
return v1
}
; check: ledbr %f0, %f0
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CEIL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %ceil_f32(f32) -> f32 {
block0(v0: f32):
v1 = ceil v0
return v1
}
; check: fiebr %f0, %f0, 6
; nextln: br %r14
function %ceil_f64(f64) -> f64 {
block0(v0: f64):
v1 = ceil v0
return v1
}
; check: fidbr %f0, %f0, 6
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FLOOR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %floor_f32(f32) -> f32 {
block0(v0: f32):
v1 = floor v0
return v1
}
; check: fiebr %f0, %f0, 7
; nextln: br %r14
function %floor_f64(f64) -> f64 {
block0(v0: f64):
v1 = floor v0
return v1
}
; check: fidbr %f0, %f0, 7
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TRUNC
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %trunc_f32(f32) -> f32 {
block0(v0: f32):
v1 = trunc v0
return v1
}
; check: fiebr %f0, %f0, 5
; nextln: br %r14
function %trunc_f64(f64) -> f64 {
block0(v0: f64):
v1 = trunc v0
return v1
}
; check: fidbr %f0, %f0, 5
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; NEAREST
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %nearest_f32(f32) -> f32 {
block0(v0: f32):
v1 = nearest v0
return v1
}
; check: fiebr %f0, %f0, 4
; nextln: br %r14
function %nearest_f64(f64) -> f64 {
block0(v0: f64):
v1 = nearest v0
return v1
}
; check: fidbr %f0, %f0, 4
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FMA
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fma_f32(f32, f32, f32) -> f32 {
block0(v0: f32, v1: f32, v2: f32):
v3 = fma v0, v1, v2
return v3
}
; FIXME: regalloc
; check: maebr %f4, %f0, %f2
; nextln: ler %f0, %f4
; nextln: br %r14
function %fma_f64(f64, f64, f64) -> f64 {
block0(v0: f64, v1: f64, v2: f64):
v3 = fma v0, v1, v2
return v3
}
; check: madbr %f4, %f0, %f2
; nextln: ldr %f0, %f4
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FCOPYSIGN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fcopysign_f32(f32, f32) -> f32 {
block0(v0: f32, v1: f32):
v2 = fcopysign v0, v1
return v2
}
; check: cpsdr %f0, %f2, %f0
; nextln: br %r14
function %fcopysign_f64(f64, f64) -> f64 {
block0(v0: f64, v1: f64):
v2 = fcopysign v0, v1
return v2
}
; check: cpsdr %f0, %f2, %f0
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FCVT_TO_UINT/FCVT_TO_SINT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fcvt_to_uint_f32_i32(f32) -> i32 {
block0(v0: f32):
v1 = fcvt_to_uint.i32 v0
return v1
}
; check: cebr %f0, %f0
; nextln: jno 6 ; trap
; nextln: clfebr %r2, 5, %f0, 0
; nextln: jno 6 ; trap
; nextln: br %r14
function %fcvt_to_sint_f32_i32(f32) -> i32 {
block0(v0: f32):
v1 = fcvt_to_sint.i32 v0
return v1
}
; check: cebr %f0, %f0
; nextln: jno 6 ; trap
; nextln: cfebra %r2, 5, %f0, 0
; nextln: jno 6 ; trap
; nextln: br %r14
function %fcvt_to_uint_f32_i64(f32) -> i64 {
block0(v0: f32):
v1 = fcvt_to_uint.i64 v0
return v1
}
; check: cebr %f0, %f0
; nextln: jno 6 ; trap
; nextln: clgebr %r2, 5, %f0, 0
; nextln: jno 6 ; trap
; nextln: br %r14
function %fcvt_to_sint_f32_i64(f32) -> i64 {
block0(v0: f32):
v1 = fcvt_to_sint.i64 v0
return v1
}
; check: cebr %f0, %f0
; nextln: jno 6 ; trap
; nextln: cgebra %r2, 5, %f0, 0
; nextln: jno 6 ; trap
; nextln: br %r14
function %fcvt_to_uint_f64_i32(f64) -> i32 {
block0(v0: f64):
v1 = fcvt_to_uint.i32 v0
return v1
}
; check: cdbr %f0, %f0
; nextln: jno 6 ; trap
; nextln: clfdbr %r2, 5, %f0, 0
; nextln: jno 6 ; trap
; nextln: br %r14
function %fcvt_to_sint_f64_i32(f64) -> i32 {
block0(v0: f64):
v1 = fcvt_to_sint.i32 v0
return v1
}
; check: cdbr %f0, %f0
; nextln: jno 6 ; trap
; nextln: cfdbra %r2, 5, %f0, 0
; nextln: jno 6 ; trap
; nextln: br %r14
function %fcvt_to_uint_f64_i64(f64) -> i64 {
block0(v0: f64):
v1 = fcvt_to_uint.i64 v0
return v1
}
; check: cdbr %f0, %f0
; nextln: jno 6 ; trap
; nextln: clgdbr %r2, 5, %f0, 0
; nextln: jno 6 ; trap
; nextln: br %r14
function %fcvt_to_sint_f64_i64(f64) -> i64 {
block0(v0: f64):
v1 = fcvt_to_sint.i64 v0
return v1
}
; check: cdbr %f0, %f0
; nextln: jno 6 ; trap
; nextln: cgdbra %r2, 5, %f0, 0
; nextln: jno 6 ; trap
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FCVT_FROM_UINT/FCVT_FROM_SINT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fcvt_from_uint_i32_f32(i32) -> f32 {
block0(v0: i32):
v1 = fcvt_from_uint.f32 v0
return v1
}
; check: celfbr %f0, 0, %r2, 0
; nextln: br %r14
function %fcvt_from_sint_i32_f32(i32) -> f32 {
block0(v0: i32):
v1 = fcvt_from_sint.f32 v0
return v1
}
; check: cefbra %f0, 0, %r2, 0
; nextln: br %r14
function %fcvt_from_uint_i64_f32(i64) -> f32 {
block0(v0: i64):
v1 = fcvt_from_uint.f32 v0
return v1
}
; check: celgbr %f0, 0, %r2, 0
; nextln: br %r14
function %fcvt_from_sint_i64_f32(i64) -> f32 {
block0(v0: i64):
v1 = fcvt_from_sint.f32 v0
return v1
}
; check: cegbra %f0, 0, %r2, 0
; nextln: br %r14
function %fcvt_from_uint_i32_f64(i32) -> f64 {
block0(v0: i32):
v1 = fcvt_from_uint.f64 v0
return v1
}
; check: cdlfbr %f0, 0, %r2, 0
; nextln: br %r14
function %fcvt_from_sint_i32_f64(i32) -> f64 {
block0(v0: i32):
v1 = fcvt_from_sint.f64 v0
return v1
}
; check: cdfbra %f0, 0, %r2, 0
; nextln: br %r14
function %fcvt_from_uint_i64_f64(i64) -> f64 {
block0(v0: i64):
v1 = fcvt_from_uint.f64 v0
return v1
}
; check: cdlgbr %f0, 0, %r2, 0
; nextln: br %r14
function %fcvt_from_sint_i64_f64(i64) -> f64 {
block0(v0: i64):
v1 = fcvt_from_sint.f64 v0
return v1
}
; check: cdgbra %f0, 0, %r2, 0
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FCVT_TO_UINT_SAT/FCVT_TO_SINT_SAT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %fcvt_to_uint_sat_f32_i32(f32) -> i32 {
block0(v0: f32):
v1 = fcvt_to_uint_sat.i32 v0
return v1
}
; check: clfebr %r2, 5, %f0, 0
; nextln: cebr %f0, %f0
; nextln: lochio %r2, 0
; nextln: br %r14
function %fcvt_to_sint_sat_f32_i32(f32) -> i32 {
block0(v0: f32):
v1 = fcvt_to_sint_sat.i32 v0
return v1
}
; check: cfebra %r2, 5, %f0, 0
; nextln: cebr %f0, %f0
; nextln: lochio %r2, 0
; nextln: br %r14
function %fcvt_to_uint_sat_f32_i64(f32) -> i64 {
block0(v0: f32):
v1 = fcvt_to_uint_sat.i64 v0
return v1
}
; check: clgebr %r2, 5, %f0, 0
; nextln: cebr %f0, %f0
; nextln: locghio %r2, 0
; nextln: br %r14
function %fcvt_to_sint_sat_f32_i64(f32) -> i64 {
block0(v0: f32):
v1 = fcvt_to_sint_sat.i64 v0
return v1
}
; check: cgebra %r2, 5, %f0, 0
; nextln: cebr %f0, %f0
; nextln: locghio %r2, 0
; nextln: br %r14
function %fcvt_to_uint_sat_f64_i32(f64) -> i32 {
block0(v0: f64):
v1 = fcvt_to_uint_sat.i32 v0
return v1
}
; check: clfdbr %r2, 5, %f0, 0
; nextln: cdbr %f0, %f0
; nextln: lochio %r2, 0
; nextln: br %r14
function %fcvt_to_sint_sat_f64_i32(f64) -> i32 {
block0(v0: f64):
v1 = fcvt_to_sint_sat.i32 v0
return v1
}
; check: cfdbra %r2, 5, %f0, 0
; nextln: cdbr %f0, %f0
; nextln: lochio %r2, 0
; nextln: br %r14
function %fcvt_to_uint_sat_f64_i64(f64) -> i64 {
block0(v0: f64):
v1 = fcvt_to_uint_sat.i64 v0
return v1
}
; check: clgdbr %r2, 5, %f0, 0
; nextln: cdbr %f0, %f0
; nextln: locghio %r2, 0
; nextln: br %r14
function %fcvt_to_sint_sat_f64_i64(f64) -> i64 {
block0(v0: f64):
v1 = fcvt_to_sint_sat.i64 v0
return v1
}
; check: cgdbra %r2, 5, %f0, 0
; nextln: cdbr %f0, %f0
; nextln: locghio %r2, 0
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BITCAST
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %bitcast_i64_f64(i64) -> f64 {
block0(v0: i64):
v1 = bitcast.f64 v0
return v1
}
; check: ldgr %f0, %r2
; nextln: br %r14
function %bitcast_f64_i64(f64) -> i64 {
block0(v0: f64):
v1 = bitcast.i64 v0
return v1
}
; check: lgdr %r2, %f0
; nextln: br %r14
function %bitcast_i32_f32(i32) -> f32 {
block0(v0: i32):
v1 = bitcast.f32 v0
return v1
}
; check: sllg %r2, %r2, 32
; nextln: ldgr %f0, %r2
; nextln: br %r14
function %bitcast_f32_i32(f32) -> i32 {
block0(v0: f32):
v1 = bitcast.i32 v0
return v1
}
; check: lgdr %r2, %f0
; nextln: srlg %r2, %r2, 32
; nextln: br %r14

49
cranelift/filetests/filetests/isa/s390x/heap_addr.clif

@ -0,0 +1,49 @@
test compile
target s390x
function %dynamic_heap_check(i64 vmctx, i32) -> i64 {
gv0 = vmctx
gv1 = load.i32 notrap aligned gv0
heap0 = dynamic gv0, bound gv1, offset_guard 0x1000, index_type i32
block0(v0: i64, v1: i32):
v2 = heap_addr.i64 heap0, v1, 0
return v2
}
; check: Block 0:
; check: l %r4, 0(%r2)
; nextln: ahi %r4, 0
; nextln: clr %r3, %r4
; nextln: jgnh label1 ; jg label2
; check: Block 1:
; check: llgfr %r5, %r3
; nextln: agr %r2, %r5
; nextln: lghi %r5, 0
; nextln: clr %r3, %r4
; nextln: locgrh %r2, %r5
; nextln: br %r14
; check: Block 2:
; check: trap
function %static_heap_check(i64 vmctx, i32) -> i64 {
gv0 = vmctx
heap0 = static gv0, bound 0x1_0000, offset_guard 0x1000, index_type i32
block0(v0: i64, v1: i32):
v2 = heap_addr.i64 heap0, v1, 0
return v2
}
; check: Block 0:
; check: clfi %r3, 65536
; nextln: jgnh label1 ; jg label2
; check: Block 1:
; check: llgfr %r4, %r3
; nextln: agr %r2, %r4
; nextln: lghi %r4, 0
; nextln: clfi %r3, 65536
; nextln: locgrh %r2, %r4
; nextln: br %r14
; check: Block 2:
; check: trap

604
cranelift/filetests/filetests/isa/s390x/icmp.clif

@ -0,0 +1,604 @@
test compile
target s390x
function %icmp_slt_i64(i64, i64) -> b1 {
block0(v0: i64, v1: i64):
v2 = icmp.i64 slt v0, v1
return v2
}
; check: cgr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i64_ext32(i64, i32) -> b1 {
block0(v0: i64, v1: i32):
v2 = sextend.i64 v1
v3 = icmp.i64 slt v0, v2
return v3
}
; check: cgfr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i64_imm16(i64) -> b1 {
block0(v0: i64):
v1 = iconst.i64 1
v2 = icmp.i64 slt v0, v1
return v2
}
; check: cghi %r2, 1
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i64_imm32(i64) -> b1 {
block0(v0: i64):
v1 = iconst.i64 32768
v2 = icmp.i64 slt v0, v1
return v2
}
; check: cgfi %r2, 32768
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i64_mem(i64, i64) -> b1 {
block0(v0: i64, v1: i64):
v2 = load.i64 v1
v3 = icmp.i64 slt v0, v2
return v3
}
; check: cg %r2, 0(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i64_sym(i64) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
v2 = load.i64 v1
v3 = icmp.i64 slt v0, v2
return v3
}
; check: cgrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i64_mem_ext16(i64, i64) -> b1 {
block0(v0: i64, v1: i64):
v2 = sload16.i64 v1
v3 = icmp.i64 slt v0, v2
return v3
}
; check: cgh %r2, 0(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i64_sym_ext16(i64) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
v2 = sload16.i64 v1
v3 = icmp.i64 slt v0, v2
return v3
}
; check: cghrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i64_mem_ext32(i64, i64) -> b1 {
block0(v0: i64, v1: i64):
v2 = sload32.i64 v1
v3 = icmp.i64 slt v0, v2
return v3
}
; check: cgf %r2, 0(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i64_sym_ext32(i64) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
v2 = sload32.i64 v1
v3 = icmp.i64 slt v0, v2
return v3
}
; check: cgfrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i32(i32, i32) -> b1 {
block0(v0: i32, v1: i32):
v2 = icmp.i32 slt v0, v1
return v2
}
; check: cr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i32_imm16(i32) -> b1 {
block0(v0: i32):
v1 = iconst.i32 1
v2 = icmp.i32 slt v0, v1
return v2
}
; check: chi %r2, 1
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i32_imm(i32) -> b1 {
block0(v0: i32):
v1 = iconst.i32 32768
v2 = icmp.i32 slt v0, v1
return v2
}
; check: cfi %r2, 32768
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i32_mem(i32, i64) -> b1 {
block0(v0: i32, v1: i64):
v2 = load.i32 v1
v3 = icmp.i32 slt v0, v2
return v3
}
; check: c %r2, 0(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i32_memoff(i32, i64) -> b1 {
block0(v0: i32, v1: i64):
v2 = load.i32 v1+4096
v3 = icmp.i32 slt v0, v2
return v3
}
; check: cy %r2, 4096(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i32_sym(i32) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i32):
v1 = symbol_value.i64 gv0
v2 = load.i32 v1
v3 = icmp.i32 slt v0, v2
return v3
}
; check: crl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i32_mem_ext16(i32, i64) -> b1 {
block0(v0: i32, v1: i64):
v2 = sload16.i32 v1
v3 = icmp.i32 slt v0, v2
return v3
}
; check: ch %r2, 0(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i32_memoff_ext16(i32, i64) -> b1 {
block0(v0: i32, v1: i64):
v2 = sload16.i32 v1+4096
v3 = icmp.i32 slt v0, v2
return v3
}
; check: chy %r2, 4096(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i32_sym_ext16(i32) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i32):
v1 = symbol_value.i64 gv0
v2 = sload16.i32 v1
v3 = icmp.i32 slt v0, v2
return v3
}
; check: chrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i16(i16, i16) -> b1 {
block0(v0: i16, v1: i16):
v2 = icmp.i16 slt v0, v1
return v2
}
; check: lhr %r2, %r2
; nextln: lhr %r3, %r3
; nextln: cr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i16_imm(i16) -> b1 {
block0(v0: i16):
v1 = iconst.i16 1
v2 = icmp.i16 slt v0, v1
return v2
}
; check: lhr %r2, %r2
; nextln: chi %r2, 1
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i16_mem(i16, i64) -> b1 {
block0(v0: i16, v1: i64):
v2 = load.i16 v1
v3 = icmp.i16 slt v0, v2
return v3
}
; check: lhr %r2, %r2
; nextln: ch %r2, 0(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i16_sym(i16) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i16):
v1 = symbol_value.i64 gv0
v2 = load.i16 v1
v3 = icmp.i16 slt v0, v2
return v3
}
; check: lhr %r2, %r2
; nextln: chrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i8(i8, i8) -> b1 {
block0(v0: i8, v1: i8):
v2 = icmp.i8 slt v0, v1
return v2
}
; check: lbr %r2, %r2
; nextln: lbr %r3, %r3
; nextln: cr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i8_imm(i8) -> b1 {
block0(v0: i8):
v1 = iconst.i8 1
v2 = icmp.i8 slt v0, v1
return v2
}
; check: lbr %r2, %r2
; nextln: chi %r2, 1
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_slt_i8_mem(i8, i64) -> b1 {
block0(v0: i8, v1: i64):
v2 = load.i8 v1
v3 = icmp.i8 slt v0, v2
return v3
}
; check: lbr %r2, %r2
; nextln: lb %r3, 0(%r3)
; nextln: cr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i64(i64, i64) -> b1 {
block0(v0: i64, v1: i64):
v2 = icmp.i64 ult v0, v1
return v2
}
; check: clgr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i64_ext32(i64, i32) -> b1 {
block0(v0: i64, v1: i32):
v2 = uextend.i64 v1
v3 = icmp.i64 ult v0, v2
return v3
}
; check: clgfr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i64_imm(i64) -> b1 {
block0(v0: i64):
v1 = iconst.i64 1
v2 = icmp.i64 ult v0, v1
return v2
}
; check: clgfi %r2, 1
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i64_mem(i64, i64) -> b1 {
block0(v0: i64, v1: i64):
v2 = load.i64 v1
v3 = icmp.i64 ult v0, v2
return v3
}
; check: clg %r2, 0(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i64_sym(i64) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
v2 = load.i64 v1
v3 = icmp.i64 ult v0, v2
return v3
}
; check: clgrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i64_mem_ext32(i64, i64) -> b1 {
block0(v0: i64, v1: i64):
v2 = uload32.i64 v1
v3 = icmp.i64 ult v0, v2
return v3
}
; check: clgf %r2, 0(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i64_sym_ext32(i64) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
v2 = uload32.i64 v1
v3 = icmp.i64 ult v0, v2
return v3
}
; check: clgfrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i64_sym_ext16(i64) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
v2 = uload16.i64 v1
v3 = icmp.i64 ult v0, v2
return v3
}
; check: clghrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i32(i32, i32) -> b1 {
block0(v0: i32, v1: i32):
v2 = icmp.i32 ult v0, v1
return v2
}
; check: clr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i32_imm(i32) -> b1 {
block0(v0: i32):
v1 = iconst.i32 1
v2 = icmp.i32 ult v0, v1
return v2
}
; check: clfi %r2, 1
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i32_mem(i32, i64) -> b1 {
block0(v0: i32, v1: i64):
v2 = load.i32 v1
v3 = icmp.i32 ult v0, v2
return v3
}
; check: cl %r2, 0(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i32_memoff(i32, i64) -> b1 {
block0(v0: i32, v1: i64):
v2 = load.i32 v1+4096
v3 = icmp.i32 ult v0, v2
return v3
}
; check: cly %r2, 4096(%r3)
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i32_sym(i32) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i32):
v1 = symbol_value.i64 gv0
v2 = load.i32 v1
v3 = icmp.i32 ult v0, v2
return v3
}
; check: clrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i32_sym_ext16(i32) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i32):
v1 = symbol_value.i64 gv0
v2 = uload16.i32 v1
v3 = icmp.i32 ult v0, v2
return v3
}
; check: clhrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i16(i16, i16) -> b1 {
block0(v0: i16, v1: i16):
v2 = icmp.i16 ult v0, v1
return v2
}
; check: llhr %r2, %r2
; nextln: llhr %r3, %r3
; nextln: clr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i16_imm(i16) -> b1 {
block0(v0: i16):
v1 = iconst.i16 1
v2 = icmp.i16 ult v0, v1
return v2
}
; check: llhr %r2, %r2
; nextln: clfi %r2, 1
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i16_mem(i16, i64) -> b1 {
block0(v0: i16, v1: i64):
v2 = load.i16 v1
v3 = icmp.i16 ult v0, v2
return v3
}
; check: llhr %r2, %r2
; nextln: llh %r3, 0(%r3)
; nextln: clr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i16_mem(i16) -> b1 {
gv0 = symbol colocated %sym
block0(v0: i16):
v1 = symbol_value.i64 gv0
v2 = load.i16 v1
v3 = icmp.i16 ult v0, v2
return v3
}
; check: llhr %r2, %r2
; nextln: clhrl %r2, %sym + 0
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i8(i8, i8) -> b1 {
block0(v0: i8, v1: i8):
v2 = icmp.i8 ult v0, v1
return v2
}
; check: llcr %r2, %r2
; nextln: llcr %r3, %r3
; nextln: clr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i8_imm(i8) -> b1 {
block0(v0: i8):
v1 = iconst.i8 1
v2 = icmp.i8 ult v0, v1
return v2
}
; check: llcr %r2, %r2
; nextln: clfi %r2, 1
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14
function %icmp_ult_i8_mem(i8, i64) -> b1 {
block0(v0: i8, v1: i64):
v2 = load.i8 v1
v3 = icmp.i8 ult v0, v2
return v3
}
; check: llcr %r2, %r2
; nextln: llc %r3, 0(%r3)
; nextln: clr %r2, %r3
; nextln: lhi %r2, 0
; nextln: lochil %r2, 1
; nextln: br %r14

45
cranelift/filetests/filetests/isa/s390x/jumptable.clif

@ -0,0 +1,45 @@
test compile
target s390x
function %f(i64) -> i64 {
jt0 = jump_table [block1, block2, block3]
block0(v0: i64):
br_table v0, block4, jt0
block1:
v1 = iconst.i64 1
jump block5(v1)
block2:
v2 = iconst.i64 2
jump block5(v2)
block3:
v3 = iconst.i64 3
jump block5(v3)
block4:
v4 = iconst.i64 4
jump block5(v4)
block5(v5: i64):
v6 = iadd.i64 v0, v5
return v6
}
; check: clgfi %r2, 3 ; jghe label1 ; sllg %r4, %r2, 2 ; larl %r3, 18 ; lgf %r4, 0(%r4, %r3) ; agrk %r3, %r3, %r4 ; br %r3 ; jt_entries
; check: lghi %r3, 1
; nextln: jg
; check: lghi %r3, 2
; nextln: jg
; check: lghi %r3, 3
; nextln: jg
; check: agr %r2, %r3
; nextln: br %r14

258
cranelift/filetests/filetests/isa/s390x/load-little.clif

@ -0,0 +1,258 @@
test compile
target s390x
function %load_i64(i64) -> i64 {
block0(v0: i64):
v1 = load.i64 little v0
return v1
}
; check: lrvg %r2, 0(%r2)
; nextln: br %r14
function %load_i64_sym() -> i64 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = load.i64 little v0
return v1
}
; check: larl %r1, %sym + 0 ; lrvg %r2, 0(%r1)
; nextln: br %r14
function %uload8_i64(i64) -> i64 {
block0(v0: i64):
v1 = uload8.i64 little v0
return v1
}
; check: llgc %r2, 0(%r2)
; nextln: br %r14
function %sload8_i64(i64) -> i64 {
block0(v0: i64):
v1 = sload8.i64 little v0
return v1
}
; check: lgb %r2, 0(%r2)
; nextln: br %r14
function %uload16_i64(i64) -> i64 {
block0(v0: i64):
v1 = uload16.i64 little v0
return v1
}
; check: lrvh %r2, 0(%r2)
; nextln: llghr %r2, %r2
; nextln: br %r14
function %uload16_i64_sym() -> i64 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = uload16.i64 little v0
return v1
}
; check: larl %r1, %sym + 0 ; lrvh %r2, 0(%r1)
; nextln: llghr %r2, %r2
; nextln: br %r14
function %sload16_i64(i64) -> i64 {
block0(v0: i64):
v1 = sload16.i64 little v0
return v1
}
; check: lrvh %r2, 0(%r2)
; nextln: lghr %r2, %r2
; nextln: br %r14
function %sload16_i64_sym() -> i64 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = sload16.i64 little v0
return v1
}
; check: larl %r1, %sym + 0 ; lrvh %r2, 0(%r1)
; nextln: lghr %r2, %r2
; nextln: br %r14
function %uload32_i64(i64) -> i64 {
block0(v0: i64):
v1 = uload32.i64 little v0
return v1
}
; check: lrv %r2, 0(%r2)
; nextln: llgfr %r2, %r2
; nextln: br %r14
function %uload32_i64_sym() -> i64 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = uload32.i64 little v0
return v1
}
; check: larl %r1, %sym + 0 ; lrv %r2, 0(%r1)
; nextln: llgfr %r2, %r2
; nextln: br %r14
function %sload32_i64(i64) -> i64 {
block0(v0: i64):
v1 = sload32.i64 little v0
return v1
}
; check: lrv %r2, 0(%r2)
; nextln: lgfr %r2, %r2
; nextln: br %r14
function %sload32_i64_sym() -> i64 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = sload32.i64 little v0
return v1
}
; check: larl %r1, %sym + 0 ; lrv %r2, 0(%r1)
; nextln: lgfr %r2, %r2
; nextln: br %r14
function %load_i32(i64) -> i32 {
block0(v0: i64):
v1 = load.i32 little v0
return v1
}
; check: lrv %r2, 0(%r2)
; nextln: br %r14
function %load_i32_sym() -> i32 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = load.i32 little v0
return v1
}
; check: larl %r1, %sym + 0 ; lrv %r2, 0(%r1)
; nextln: br %r14
function %uload8_i32(i64) -> i32 {
block0(v0: i64):
v1 = uload8.i32 little v0
return v1
}
; check: llc %r2, 0(%r2)
; nextln: br %r14
function %sload8_i32(i64) -> i32 {
block0(v0: i64):
v1 = sload8.i32 little v0
return v1
}
; check: lb %r2, 0(%r2)
; nextln: br %r14
function %uload16_i32(i64) -> i32 {
block0(v0: i64):
v1 = uload16.i32 little v0
return v1
}
; check: lrvh %r2, 0(%r2)
; nextln: llhr %r2, %r2
; nextln: br %r14
function %uload16_i32_sym() -> i32 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = uload16.i32 little v0
return v1
}
; check: larl %r1, %sym + 0 ; lrvh %r2, 0(%r1)
; nextln: llhr %r2, %r2
; nextln: br %r14
function %sload16_i32(i64) -> i32 {
block0(v0: i64):
v1 = sload16.i32 little v0
return v1
}
; check: lrvh %r2, 0(%r2)
; nextln: lhr %r2, %r2
; nextln: br %r14
function %sload16_i32_sym() -> i32 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = sload16.i32 little v0
return v1
}
; check: larl %r1, %sym + 0 ; lrvh %r2, 0(%r1)
; nextln: lhr %r2, %r2
; nextln: br %r14
function %load_i16(i64) -> i16 {
block0(v0: i64):
v1 = load.i16 little v0
return v1
}
; check: lrvh %r2, 0(%r2)
; nextln: br %r14
function %load_i16_sym() -> i16 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = load.i16 little v0
return v1
}
; check: larl %r1, %sym + 0 ; lrvh %r2, 0(%r1)
; nextln: br %r14
function %uload8_i16(i64) -> i16 {
block0(v0: i64):
v1 = uload8.i16 little v0
return v1
}
; check: llc %r2, 0(%r2)
; nextln: br %r14
function %sload8_i16(i64) -> i16 {
block0(v0: i64):
v1 = sload8.i16 little v0
return v1
}
; check: lb %r2, 0(%r2)
; nextln: br %r14
function %load_i8(i64) -> i8 {
block0(v0: i64):
v1 = load.i8 little v0
return v1
}
; check: llc %r2, 0(%r2)
; nextln: br %r14

264
cranelift/filetests/filetests/isa/s390x/load.clif

@ -0,0 +1,264 @@
test compile
target s390x
function %load_i64(i64) -> i64 {
block0(v0: i64):
v1 = load.i64 v0
return v1
}
; check: lg %r2, 0(%r2)
; nextln: br %r14
function %load_i64_sym() -> i64 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = load.i64 v0
return v1
}
; check: lgrl %r2, %sym + 0
; nextln: br %r14
function %uload8_i64(i64) -> i64 {
block0(v0: i64):
v1 = uload8.i64 v0
return v1
}
; check: llgc %r2, 0(%r2)
; nextln: br %r14
function %sload8_i64(i64) -> i64 {
block0(v0: i64):
v1 = sload8.i64 v0
return v1
}
; check: lgb %r2, 0(%r2)
; nextln: br %r14
function %uload16_i64(i64) -> i64 {
block0(v0: i64):
v1 = uload16.i64 v0
return v1
}
; check: llgh %r2, 0(%r2)
; nextln: br %r14
function %uload16_i64_sym() -> i64 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = uload16.i64 v0
return v1
}
; check: llghrl %r2, %sym + 0
; nextln: br %r14
function %sload16_i64(i64) -> i64 {
block0(v0: i64):
v1 = sload16.i64 v0
return v1
}
; check: lgh %r2, 0(%r2)
; nextln: br %r14
function %sload16_i64_sym() -> i64 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = sload16.i64 v0
return v1
}
; check: lghrl %r2, %sym + 0
; nextln: br %r14
function %uload32_i64(i64) -> i64 {
block0(v0: i64):
v1 = uload32.i64 v0
return v1
}
; check: llgf %r2, 0(%r2)
; nextln: br %r14
function %uload32_i64_sym() -> i64 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = uload32.i64 v0
return v1
}
; check: llgfrl %r2, %sym + 0
; nextln: br %r14
function %sload32_i64(i64) -> i64 {
block0(v0: i64):
v1 = sload32.i64 v0
return v1
}
; check: lgf %r2, 0(%r2)
; nextln: br %r14
function %sload32_i64_sym() -> i64 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = sload32.i64 v0
return v1
}
; check: lgfrl %r2, %sym + 0
; nextln: br %r14
function %load_i32(i64) -> i32 {
block0(v0: i64):
v1 = load.i32 v0
return v1
}
; check: l %r2, 0(%r2)
; nextln: br %r14
function %load_i32_sym() -> i32 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = load.i32 v0
return v1
}
; check: lrl %r2, %sym + 0
; nextln: br %r14
function %load_i32_off(i64) -> i32 {
block0(v0: i64):
v1 = load.i32 v0+4096
return v1
}
; check: ly %r2, 4096(%r2)
; nextln: br %r14
function %uload8_i32(i64) -> i32 {
block0(v0: i64):
v1 = uload8.i32 v0
return v1
}
; check: llc %r2, 0(%r2)
; nextln: br %r14
function %sload8_i32(i64) -> i32 {
block0(v0: i64):
v1 = sload8.i32 v0
return v1
}
; check: lb %r2, 0(%r2)
; nextln: br %r14
function %uload16_i32(i64) -> i32 {
block0(v0: i64):
v1 = uload16.i32 v0
return v1
}
; check: llh %r2, 0(%r2)
; nextln: br %r14
function %uload16_i32_sym() -> i32 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = uload16.i32 v0
return v1
}
; check: llhrl %r2, %sym + 0
; nextln: br %r14
function %sload16_i32(i64) -> i32 {
block0(v0: i64):
v1 = sload16.i32 v0
return v1
}
; check: lh %r2, 0(%r2)
; nextln: br %r14
function %sload16_i32_off(i64) -> i32 {
block0(v0: i64):
v1 = sload16.i32 v0+4096
return v1
}
; check: lhy %r2, 4096(%r2)
; nextln: br %r14
function %sload16_i32_sym() -> i32 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = sload16.i32 v0
return v1
}
; check: lhrl %r2, %sym + 0
; nextln: br %r14
function %load_i16(i64) -> i16 {
block0(v0: i64):
v1 = load.i16 v0
return v1
}
; check: llh %r2, 0(%r2)
; nextln: br %r14
function %load_i16_sym() -> i16 {
gv0 = symbol colocated %sym
block0:
v0 = symbol_value.i64 gv0
v1 = load.i16 v0
return v1
}
; check: llhrl %r2, %sym + 0
; nextln: br %r14
function %uload8_i16(i64) -> i16 {
block0(v0: i64):
v1 = uload8.i16 v0
return v1
}
; check: llc %r2, 0(%r2)
; nextln: br %r14
function %sload8_i16(i64) -> i16 {
block0(v0: i64):
v1 = sload8.i16 v0
return v1
}
; check: lb %r2, 0(%r2)
; nextln: br %r14
function %load_i8(i64) -> i8 {
block0(v0: i64):
v1 = load.i8 v0
return v1
}
; check: llc %r2, 0(%r2)
; nextln: br %r14

79
cranelift/filetests/filetests/isa/s390x/multivalue-ret.clif

@ -0,0 +1,79 @@
test compile
target s390x
;; Test default (non-SpiderMonkey) ABI.
function %f1() -> i64, i64, i64, i64 {
block1:
v0 = iconst.i64 1
v1 = iconst.i64 2
v2 = iconst.i64 3
v3 = iconst.i64 4
return v0, v1, v2, v3
}
; check: lghi %r2, 1
; nextln: lghi %r3, 2
; nextln: lghi %r4, 3
; nextln: lghi %r5, 4
; nextln: br %r14
function %f1() -> i64, i64, i64, i64, i64, i64 {
block1:
v0 = iconst.i64 1
v1 = iconst.i64 2
v2 = iconst.i64 3
v3 = iconst.i64 4
v4 = iconst.i64 5
v5 = iconst.i64 6
return v0, v1, v2, v3, v4, v5
}
; check: stmg %r12, %r15, 96(%r15)
; nextln: lgr %r14, %r2
; nextln: lghi %r2, 1
; nextln: lghi %r3, 2
; nextln: lghi %r4, 3
; nextln: lghi %r5, 4
; nextln: lghi %r13, 5
; nextln: lghi %r12, 6
; nextln: stg %r13, 0(%r14)
; nextln: stg %r12, 8(%r14)
; nextln: lmg %r12, %r15, 96(%r15)
; nextln: br %r14
;; Test default (non-SpiderMonkey) ABI.
function %f3() -> f64, f64, f64, f64 {
block1:
v0 = f64const 0x0.0
v1 = f64const 0x1.0
v2 = f64const 0x2.0
v3 = f64const 0x3.0
return v0, v1, v2, v3
}
; check: bras %r1, 12 ; data.f64 0 ; ld %f0, 0(%r1)
; nextln: bras %r1, 12 ; data.f64 1 ; ld %f2, 0(%r1)
; nextln: bras %r1, 12 ; data.f64 2 ; ld %f4, 0(%r1)
; nextln: bras %r1, 12 ; data.f64 3 ; ld %f6, 0(%r1)
; nextln: br %r14
function %f4() -> f64, f64, f64, f64, f64, f64 {
block1:
v0 = f64const 0x0.0
v1 = f64const 0x1.0
v2 = f64const 0x2.0
v3 = f64const 0x3.0
v4 = f64const 0x4.0
v5 = f64const 0x5.0
return v0, v1, v2, v3, v4, v5
}
; check: bras %r1, 12 ; data.f64 0 ; ld %f0, 0(%r1)
; nextln: bras %r1, 12 ; data.f64 1 ; ld %f2, 0(%r1)
; nextln: bras %r1, 12 ; data.f64 2 ; ld %f4, 0(%r1)
; nextln: bras %r1, 12 ; data.f64 3 ; ld %f6, 0(%r1)
; nextln: bras %r1, 12 ; data.f64 4 ; ld %f1, 0(%r1)
; nextln: bras %r1, 12 ; data.f64 5 ; ld %f3, 0(%r1)
; nextln: std %f1, 0(%r2)
; nextln: std %f3, 8(%r2)
; nextln: br %r14

101
cranelift/filetests/filetests/isa/s390x/reftypes.clif

@ -0,0 +1,101 @@
test compile
target s390x
function %f0(r64, r64) -> r64 {
block0(v0: r64, v1: r64):
return v1
}
; check: lgr %r2, %r3
; nextln: br %r14
function %f1(r64) -> b1 {
block0(v0: r64):
v1 = is_null v0
return v1
}
; check: cghi %r2, 0
; nextln: lhi %r2, 0
; nextln: lochie %r2, 1
; nextln: br %r14
function %f2(r64) -> b1 {
block0(v0: r64):
v1 = is_invalid v0
return v1
}
; check: cghi %r2, -1
; nextln: lhi %r2, 0
; nextln: lochie %r2, 1
; nextln: br %r14
function %f3() -> r64 {
block0:
v0 = null.r64
return v0
}
; check: lghi %r2, 0
; nextln: br %r14
function %f4(r64, r64) -> r64, r64, r64 {
fn0 = %f(r64) -> b1
ss0 = explicit_slot 8
block0(v0: r64, v1: r64):
v2 = call fn0(v0)
stack_store.r64 v0, ss0
brz v2, block1(v1, v0)
jump block2(v0, v1)
block1(v3: r64, v4: r64):
jump block3(v3, v4)
block2(v5: r64, v6: r64):
jump block3(v5, v6)
block3(v7: r64, v8: r64):
v9 = stack_load.r64 ss0
return v7, v8, v9
}
; check: Block 0:
; check: stmg %r12, %r15, 96(%r15)
; nextln: aghi %r15, -192
; nextln: virtual_sp_offset_adjust 160
; nextln: lgr %r13, %r2
; nextln: lgr %r12, %r3
; nextln: lgr %r2, %r13
; nextln: bras %r1, 12 ; data %f + 0 ; lg %r3, 0(%r1)
; nextln: stg %r2, 168(%r15)
; nextln: stg %r13, 176(%r15)
; nextln: stg %r12, 184(%r15)
; nextln: (safepoint: slots [S0, S1, S2]
; nextln: basr %r14, %r3
; nextln: lg %r13, 176(%r15)
; nextln: lg %r12, 184(%r15)
; nextln: la %r3, 160(%r15)
; nextln: stg %r13, 0(%r3)
; nextln: llcr %r2, %r2
; nextln: chi %r2, 0
; nextln: jgnlh label1 ; jg label3
; check: Block 1:
; check: jg label2
; check: Block 2:
; check: lgr %r2, %r12
; nextln: jg label5
; check: Block 3:
; check: jg label4
; check: Block 4:
; check: lgr %r2, %r13
; nextln: lgr %r13, %r12
; nextln: jg label5
; check: Block 5:
; check: la %r3, 160(%r15)
; nextln: lg %r3, 0(%r3)
; nextln: lgr %r4, %r3
; nextln: lgr %r3, %r13
; nextln: lmg %r12, %r15, 288(%r15)
; nextln: br %r14

12
cranelift/filetests/filetests/isa/s390x/saturating-ops.clif

@ -0,0 +1,12 @@
test compile
target s390x
; FIXME: not yet supported
function %uaddsat64(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
; v2 = uadd_sat.i64 v0, v1
v2 = iconst.i64 0
return v2
}

461
cranelift/filetests/filetests/isa/s390x/shift-rotate.clif

@ -0,0 +1,461 @@
test compile
target s390x
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ROTR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %rotr_i64_reg(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = rotr.i64 v0, v1
return v2
}
; check: lcgr %r3, %r3
; nextln: rllg %r2, %r2, 0(%r3)
; nextln: br %r14
function %rotr_i64_imm(i64) -> i64 {
block0(v0: i64):
v1 = iconst.i32 17
v2 = rotr.i64 v0, v1
return v2
}
; check: rllg %r2, %r2, 47
; nextln: br %r14
function %rotr_i32_reg(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = rotr.i32 v0, v1
return v2
}
; check: lcr %r3, %r3
; nextln: rll %r2, %r2, 0(%r3)
; nextln: br %r14
function %rotr_i32_imm(i32) -> i32 {
block0(v0: i32):
v1 = iconst.i32 17
v2 = rotr.i32 v0, v1
return v2
}
; check: rll %r2, %r2, 15
; nextln: br %r14
function %rotr_i16_reg(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = rotr.i16 v0, v1
return v2
}
; check: llhr %r2, %r2
; nextln: lr %r5, %r3
; nextln: lcr %r4, %r3
; nextln: nill %r5, 15
; nextln: nill %r4, 15
; nextln: sllk %r3, %r2, 0(%r5)
; nextln: srlk %r2, %r2, 0(%r4)
; nextln: ork %r2, %r3, %r2
; nextln: br %r14
function %rotr_i16_imm(i16) -> i16 {
block0(v0: i16):
v1 = iconst.i32 10
v2 = rotr.i16 v0, v1
return v2
}
; check: llhr %r2, %r2
; nextln: sllk %r3, %r2, 6
; nextln: srlk %r2, %r2, 10
; nextln: ork %r2, %r3, %r2
; nextln: br %r14
function %rotr_i8_reg(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = rotr.i8 v0, v1
return v2
}
; check: llcr %r2, %r2
; nextln: lr %r5, %r3
; nextln: lcr %r4, %r3
; nextln: nill %r5, 7
; nextln: nill %r4, 7
; nextln: sllk %r3, %r2, 0(%r5)
; nextln: srlk %r2, %r2, 0(%r4)
; nextln: ork %r2, %r3, %r2
; nextln: br %r14
function %rotr_i8_imm(i8) -> i8 {
block0(v0: i8):
v1 = iconst.i32 3
v2 = rotr.i8 v0, v1
return v2
}
; check: llcr %r2, %r2
; nextln: sllk %r3, %r2, 5
; nextln: srlk %r2, %r2, 3
; nextln: ork %r2, %r3, %r2
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ROTL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %rotl_i64_reg(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = rotl.i64 v0, v1
return v2
}
; check: rllg %r2, %r2, 0(%r3)
; nextln: br %r14
function %rotl_i64_imm(i64) -> i64 {
block0(v0: i64):
v1 = iconst.i32 17
v2 = rotl.i64 v0, v1
return v2
}
; check: rllg %r2, %r2, 17
; nextln: br %r14
function %rotl_i32_reg(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = rotl.i32 v0, v1
return v2
}
; check: rll %r2, %r2, 0(%r3)
; nextln: br %r14
function %rotl_i32_imm(i32) -> i32 {
block0(v0: i32):
v1 = iconst.i32 17
v2 = rotl.i32 v0, v1
return v2
}
; check: rll %r2, %r2, 17
; nextln: br %r14
function %rotl_i16_reg(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = rotl.i16 v0, v1
return v2
}
; check: llhr %r2, %r2
; nextln: lr %r4, %r3
; nextln: lcr %r3, %r3
; nextln: nill %r4, 15
; nextln: nill %r3, 15
; nextln: sllk %r3, %r2, 0(%r3)
; nextln: srlk %r2, %r2, 0(%r4)
; nextln: ork %r2, %r3, %r2
; nextln: br %r14
function %rotl_i16_imm(i16) -> i16 {
block0(v0: i16):
v1 = iconst.i32 10
v2 = rotl.i16 v0, v1
return v2
}
; check: llhr %r2, %r2
; nextln: sllk %r3, %r2, 10
; nextln: srlk %r2, %r2, 6
; nextln: ork %r2, %r3, %r2
; nextln: br %r14
function %rotl_i8_reg(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = rotl.i8 v0, v1
return v2
}
; check: llcr %r2, %r2
; nextln: lr %r4, %r3
; nextln: lcr %r3, %r3
; nextln: nill %r4, 7
; nextln: nill %r3, 7
; nextln: sllk %r3, %r2, 0(%r3)
; nextln: srlk %r2, %r2, 0(%r4)
; nextln: ork %r2, %r3, %r2
; nextln: br %r14
function %rotr_i8_imm(i8) -> i8 {
block0(v0: i8):
v1 = iconst.i32 3
v2 = rotl.i8 v0, v1
return v2
}
; check: llcr %r2, %r2
; nextln: sllk %r3, %r2, 3
; nextln: srlk %r2, %r2, 5
; nextln: ork %r2, %r3, %r2
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; USHR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %ushr_i64_reg(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = ushr.i64 v0, v1
return v2
}
; check: srlg %r2, %r2, 0(%r3)
; nextln: br %r14
function %ushr_i64_imm(i64) -> i64 {
block0(v0: i64):
v1 = iconst.i32 17
v2 = ushr.i64 v0, v1
return v2
}
; check: srlg %r2, %r2, 17
; nextln: br %r14
function %ushr_i32_reg(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = ushr.i32 v0, v1
return v2
}
; check: srlk %r2, %r2, 0(%r3)
; nextln: br %r14
function %ushr_i32_imm(i32) -> i32 {
block0(v0: i32):
v1 = iconst.i32 17
v2 = ushr.i32 v0, v1
return v2
}
; check: srlk %r2, %r2, 17
; nextln: br %r14
function %ushr_i16_reg(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = ushr.i16 v0, v1
return v2
}
; FIXME: check shift count ?
; check: llhr %r2, %r2
; nextln: nill %r3, 31
; nextln: srlk %r2, %r2, 0(%r3)
; nextln: br %r14
function %ushr_i16_imm(i16) -> i16 {
block0(v0: i16):
v1 = iconst.i32 10
v2 = ushr.i16 v0, v1
return v2
}
; check: llhr %r2, %r2
; nextln: srlk %r2, %r2, 10
; nextln: br %r14
function %ushr_i8_reg(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = ushr.i8 v0, v1
return v2
}
; check: llcr %r2, %r2
; nextln: nill %r3, 31
; nextln: srlk %r2, %r2, 0(%r3)
; nextln: br %r14
function %ushr_i8_imm(i8) -> i8 {
block0(v0: i8):
v1 = iconst.i32 3
v2 = ushr.i8 v0, v1
return v2
}
; check: llcr %r2, %r2
; nextln: srlk %r2, %r2, 3
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ISHL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %ishl_i64_reg(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = ishl.i64 v0, v1
return v2
}
; check: sllg %r2, %r2, 0(%r3)
; nextln: br %r14
function %ishl_i64_imm(i64) -> i64 {
block0(v0: i64):
v1 = iconst.i32 17
v2 = ishl.i64 v0, v1
return v2
}
; check: sllg %r2, %r2, 17
; nextln: br %r14
function %ishl_i32_reg(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = ishl.i32 v0, v1
return v2
}
; check: sllk %r2, %r2, 0(%r3)
; nextln: br %r14
function %ishl_i32_imm(i32) -> i32 {
block0(v0: i32):
v1 = iconst.i32 17
v2 = ishl.i32 v0, v1
return v2
}
; check: sllk %r2, %r2, 17
; nextln: br %r14
function %ishl_i16_reg(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = ishl.i16 v0, v1
return v2
}
; check: nill %r3, 31
; nextln: sllk %r2, %r2, 0(%r3)
; nextln: br %r14
function %ishl_i16_imm(i16) -> i16 {
block0(v0: i16):
v1 = iconst.i32 10
v2 = ishl.i16 v0, v1
return v2
}
; check: sllk %r2, %r2, 10
; nextln: br %r14
function %ishl_i8_reg(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = ishl.i8 v0, v1
return v2
}
; check: nill %r3, 31
; nextln: sllk %r2, %r2, 0(%r3)
; nextln: br %r14
function %ishl_i8_imm(i8) -> i8 {
block0(v0: i8):
v1 = iconst.i32 3
v2 = ishl.i8 v0, v1
return v2
}
; check: sllk %r2, %r2, 3
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SSHR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %sshr_i64_reg(i64, i64) -> i64 {
block0(v0: i64, v1: i64):
v2 = sshr.i64 v0, v1
return v2
}
; check: srag %r2, %r2, 0(%r3)
; nextln: br %r14
function %sshr_i64_imm(i64) -> i64 {
block0(v0: i64):
v1 = iconst.i32 17
v2 = sshr.i64 v0, v1
return v2
}
; check: srag %r2, %r2, 17
; nextln: br %r14
function %sshr_i32_reg(i32, i32) -> i32 {
block0(v0: i32, v1: i32):
v2 = sshr.i32 v0, v1
return v2
}
; check: srak %r2, %r2, 0(%r3)
; nextln: br %r14
function %sshr_i32_imm(i32) -> i32 {
block0(v0: i32):
v1 = iconst.i32 17
v2 = sshr.i32 v0, v1
return v2
}
; check: srak %r2, %r2, 17
; nextln: br %r14
function %sshr_i16_reg(i16, i16) -> i16 {
block0(v0: i16, v1: i16):
v2 = sshr.i16 v0, v1
return v2
}
; check: lhr %r2, %r2
; nextln: nill %r3, 31
; nextln: srak %r2, %r2, 0(%r3)
; nextln: br %r14
function %sshr_i16_imm(i16) -> i16 {
block0(v0: i16):
v1 = iconst.i32 10
v2 = sshr.i16 v0, v1
return v2
}
; check: lhr %r2, %r2
; nextln: srak %r2, %r2, 10
; nextln: br %r14
function %sshr_i8_reg(i8, i8) -> i8 {
block0(v0: i8, v1: i8):
v2 = sshr.i8 v0, v1
return v2
}
; check: lbr %r2, %r2
; nextln: nill %r3, 31
; nextln: srak %r2, %r2, 0(%r3)
; nextln: br %r14
function %sshr_i8_imm(i8) -> i8 {
block0(v0: i8):
v1 = iconst.i32 3
v2 = sshr.i8 v0, v1
return v2
}
; check: lbr %r2, %r2
; nextln: srak %r2, %r2, 3
; nextln: br %r14

175
cranelift/filetests/filetests/isa/s390x/stack-limit.clif

@ -0,0 +1,175 @@
test compile
target s390x
function %foo() {
block0:
return
}
function %stack_limit_leaf_zero(i64 stack_limit) {
block0(v0: i64):
return
}
; check: br %r14
function %stack_limit_gv_leaf_zero(i64 vmctx) {
gv0 = vmctx
gv1 = load.i64 notrap aligned gv0
gv2 = load.i64 notrap aligned gv1+4
stack_limit = gv2
block0(v0: i64):
return
}
; check: br %r14
function %stack_limit_call_zero(i64 stack_limit) {
fn0 = %foo()
block0(v0: i64):
call fn0()
return
}
; check: clgrtle %r15, %r2
; nextln: stmg %r14, %r15, 112(%r15)
; nextln: aghi %r15, -160
; nextln: virtual_sp_offset_adjust 160
; nextln: bras %r1, 12 ; data %foo + 0 ; lg %r2, 0(%r1)
; nextln: basr %r14, %r2
; nextln: lmg %r14, %r15, 272(%r15)
; nextln: br %r14
function %stack_limit_gv_call_zero(i64 vmctx) {
gv0 = vmctx
gv1 = load.i64 notrap aligned gv0
gv2 = load.i64 notrap aligned gv1+4
stack_limit = gv2
fn0 = %foo()
block0(v0: i64):
call fn0()
return
}
; check: lg %r1, 0(%r2)
; nextln: lg %r1, 4(%r1)
; nextln: clgrtle %r15, %r1
; nextln: stmg %r14, %r15, 112(%r15)
; nextln: aghi %r15, -160
; nextln: virtual_sp_offset_adjust 160
; nextln: bras %r1, 12 ; data %foo + 0 ; lg %r2, 0(%r1)
; nextln: basr %r14, %r2
; nextln: lmg %r14, %r15, 272(%r15)
; nextln: br %r14
function %stack_limit(i64 stack_limit) {
ss0 = explicit_slot 168
block0(v0: i64):
return
}
; check: la %r1, 168(%r2)
; nextln: clgrtle %r15, %r1
; nextln: aghi %r15, -168
; nextln: aghi %r15, 168
; nextln: br %r14
function %large_stack_limit(i64 stack_limit) {
ss0 = explicit_slot 400000
block0(v0: i64):
return
}
; check: clgrtle %r15, %r2
; nextln: lay %r1, 400000(%r2)
; nextln: clgrtle %r15, %r1
; nextln: agfi %r15, -400000
; nextln: agfi %r15, 400000
; nextln: br %r14
function %huge_stack_limit(i64 stack_limit) {
ss0 = explicit_slot 4000000
block0(v0: i64):
return
}
; check: clgrtle %r15, %r2
; nextln: lgr %r1, %r2
; nextln: algfi %r1, 4000000
; nextln: clgrtle %r15, %r1
; nextln: agfi %r15, -4000000
; nextln: agfi %r15, 4000000
; nextln: br %r14
function %limit_preamble(i64 vmctx) {
gv0 = vmctx
gv1 = load.i64 notrap aligned gv0
gv2 = load.i64 notrap aligned gv1+4
stack_limit = gv2
ss0 = explicit_slot 20
block0(v0: i64):
return
}
; check: lg %r1, 0(%r2)
; nextln: lg %r1, 4(%r1)
; nextln: la %r1, 24(%r1)
; nextln: clgrtle %r15, %r1
; nextln: aghi %r15, -24
; nextln: aghi %r15, 24
; nextln: br %r14
function %limit_preamble_large(i64 vmctx) {
gv0 = vmctx
gv1 = load.i64 notrap aligned gv0
gv2 = load.i64 notrap aligned gv1+4
stack_limit = gv2
ss0 = explicit_slot 400000
block0(v0: i64):
return
}
; check: lg %r1, 0(%r2)
; nextln: lg %r1, 4(%r1)
; nextln: clgrtle %r15, %r1
; nextln: lay %r1, 400000(%r1)
; nextln: clgrtle %r15, %r1
; nextln: agfi %r15, -400000
; nextln: agfi %r15, 400000
; nextln: br %r14
function %limit_preamble_huge(i64 vmctx) {
gv0 = vmctx
gv1 = load.i64 notrap aligned gv0
gv2 = load.i64 notrap aligned gv1+4
stack_limit = gv2
ss0 = explicit_slot 4000000
block0(v0: i64):
return
}
; check: lg %r1, 0(%r2)
; nextln: lg %r1, 4(%r1)
; nextln: clgrtle %r15, %r1
; nextln: algfi %r1, 4000000
; nextln: clgrtle %r15, %r1
; nextln: agfi %r15, -4000000
; nextln: agfi %r15, 4000000
; nextln: br %r14
function %limit_preamble_huge_offset(i64 vmctx) {
gv0 = vmctx
gv1 = load.i64 notrap aligned gv0+1000000
stack_limit = gv1
ss0 = explicit_slot 20
block0(v0: i64):
return
}
; check: lgfi %r1, 1000000 ; lg %r1, 0(%r1,%r2)
; nextln: la %r1, 24(%r1)
; nextln: clgrtle %r15, %r1
; nextln: aghi %r15, -24
; nextln: aghi %r15, 24
; nextln: br %r14

93
cranelift/filetests/filetests/isa/s390x/stack.clif

@ -0,0 +1,93 @@
test compile
target s390x
; FIXME: Should allocate register save area.
function %stack_addr_small() -> i64 {
ss0 = explicit_slot 8
block0:
v0 = stack_addr.i64 ss0
return v0
}
; check: aghi %r15, -8
; nextln: la %r2, 0(%r15)
; nextln: aghi %r15, 8
; nextln: br %r14
function %stack_addr_big() -> i64 {
ss0 = explicit_slot 100000
ss1 = explicit_slot 8
block0:
v0 = stack_addr.i64 ss0
return v0
}
; check: agfi %r15, -100008
; nextln: la %r2, 0(%r15)
; nextln: agfi %r15, 100008
; nextln: br %r14
; FIXME: don't use stack_addr legalization for stack_load and stack_store
function %stack_load_small() -> i64 {
ss0 = explicit_slot 8
block0:
v0 = stack_load.i64 ss0
return v0
}
; check: aghi %r15, -8
; nextln: la %r2, 0(%r15)
; nextln: lg %r2, 0(%r2)
; nextln: aghi %r15, 8
; nextln: br %r14
function %stack_load_big() -> i64 {
ss0 = explicit_slot 100000
ss1 = explicit_slot 8
block0:
v0 = stack_load.i64 ss0
return v0
}
; check: agfi %r15, -100008
; nextln: la %r2, 0(%r15)
; nextln: lg %r2, 0(%r2)
; nextln: agfi %r15, 100008
; nextln: br %r14
function %stack_store_small(i64) {
ss0 = explicit_slot 8
block0(v0: i64):
stack_store.i64 v0, ss0
return
}
; check: aghi %r15, -8
; nextln: la %r3, 0(%r15)
; nextln: stg %r2, 0(%r3)
; nextln: aghi %r15, 8
; nextln: br %r14
function %stack_store_big(i64) {
ss0 = explicit_slot 100000
ss1 = explicit_slot 8
block0(v0: i64):
stack_store.i64 v0, ss0
return
}
; check: agfi %r15, -100008
; nextln: la %r3, 0(%r15)
; nextln: stg %r2, 0(%r3)
; nextln: agfi %r15, 100008
; nextln: br %r14

281
cranelift/filetests/filetests/isa/s390x/store-little.clif

@ -0,0 +1,281 @@
test compile
target s390x
function %store_i64(i64, i64) {
block0(v0: i64, v1: i64):
store.i64 little v0, v1
return
}
; check: strvg %r2, 0(%r3)
; nextln: br %r14
function %store_i64_sym(i64) {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
store.i64 little v0, v1
return
}
; check: larl %r1, %sym + 0 ; strvg %r2, 0(%r1)
; nextln: br %r14
function %store_imm_i64(i64) {
block0(v0: i64):
v1 = iconst.i64 12345
store.i64 little v1, v0
return
}
; check: lghi %r3, 12345
; nextln: strvg %r3, 0(%r2)
; nextln: br %r14
function %istore8_i64(i64, i64) {
block0(v0: i64, v1: i64):
istore8.i64 little v0, v1
return
}
; check: stc %r2, 0(%r3)
; nextln: br %r14
function %istore8_imm_i64(i64) {
block0(v0: i64):
v1 = iconst.i64 123
istore8.i64 little v1, v0
return
}
; check: mvi 0(%r2), 123
; nextln: br %r14
function %istore16_i64(i64, i64) {
block0(v0: i64, v1: i64):
istore16.i64 little v0, v1
return
}
; check: strvh %r2, 0(%r3)
; nextln: br %r14
function %istore16_i64_sym(i64) {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
istore16.i64 little v0, v1
return
}
; check: larl %r1, %sym + 0 ; strvh %r2, 0(%r1)
; nextln: br %r14
function %istore16_imm_i64(i64) {
block0(v0: i64):
v1 = iconst.i64 12345
istore16.i64 little v1, v0
return
}
; check: mvhhi 0(%r2), 14640
; nextln: br %r14
function %istore32_i64(i64, i64) {
block0(v0: i64, v1: i64):
istore32.i64 little v0, v1
return
}
; check: strv %r2, 0(%r3)
; nextln: br %r14
function %istore32_i64_sym(i64) {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
istore32.i64 little v0, v1
return
}
; check: larl %r1, %sym + 0 ; strv %r2, 0(%r1)
; nextln: br %r14
function %istore32_imm_i64(i64) {
block0(v0: i64):
v1 = iconst.i64 12345
istore32.i64 little v1, v0
return
}
; check: lghi %r3, 12345
; nextln: strv %r3, 0(%r2)
; nextln: br %r14
function %store_i32(i32, i64) {
block0(v0: i32, v1: i64):
store.i32 little v0, v1
return
}
; check: strv %r2, 0(%r3)
; nextln: br %r14
function %store_i32_sym(i32) {
gv0 = symbol colocated %sym
block0(v0: i32):
v1 = symbol_value.i64 gv0
store.i32 little v0, v1
return
}
; check: larl %r1, %sym + 0 ; strv %r2, 0(%r1)
; nextln: br %r14
function %store_imm_i32(i64) {
block0(v0: i64):
v1 = iconst.i32 12345
store.i32 little v1, v0
return
}
; check: lhi %r3, 12345
; nextln: strv %r3, 0(%r2)
; nextln: br %r14
function %istore8_i32(i32, i64) {
block0(v0: i32, v1: i64):
istore8.i32 little v0, v1
return
}
; check: stc %r2, 0(%r3)
; nextln: br %r14
function %istore8_imm_i32(i64) {
block0(v0: i64):
v1 = iconst.i32 123
istore8.i32 little v1, v0
return
}
; check: mvi 0(%r2), 123
; nextln: br %r14
function %istore16_i32(i32, i64) {
block0(v0: i32, v1: i64):
istore16.i32 little v0, v1
return
}
; check: strvh %r2, 0(%r3)
; nextln: br %r14
function %istore16_i32_sym(i32) {
gv0 = symbol colocated %sym
block0(v0: i32):
v1 = symbol_value.i64 gv0
istore16.i32 little v0, v1
return
}
; check: larl %r1, %sym + 0 ; strvh %r2, 0(%r1)
; nextln: br %r14
function %istore16_imm_i32(i64) {
block0(v0: i64):
v1 = iconst.i32 12345
istore16.i32 little v1, v0
return
}
; check: mvhhi 0(%r2), 14640
; nextln: br %r14
function %store_i16(i16, i64) {
block0(v0: i16, v1: i64):
store.i16 little v0, v1
return
}
; check: strvh %r2, 0(%r3)
; nextln: br %r14
function %store_i16_sym(i16) {
gv0 = symbol colocated %sym
block0(v0: i16):
v1 = symbol_value.i64 gv0
store.i16 little v0, v1
return
}
; check: larl %r1, %sym + 0 ; strvh %r2, 0(%r1)
; nextln: br %r14
function %store_imm_i16(i64) {
block0(v0: i64):
v1 = iconst.i16 12345
store.i16 little v1, v0
return
}
; check: mvhhi 0(%r2), 14640
; nextln: br %r14
function %istore8_i16(i16, i64) {
block0(v0: i16, v1: i64):
istore8.i16 little v0, v1
return
}
; check: stc %r2, 0(%r3)
; nextln: br %r14
function %istore8_imm_i16(i64) {
block0(v0: i64):
v1 = iconst.i16 123
istore8.i16 little v1, v0
return
}
; check: mvi 0(%r2), 123
; nextln: br %r14
function %store_i8(i8, i64) {
block0(v0: i8, v1: i64):
store.i8 little v0, v1
return
}
; check: stc %r2, 0(%r3)
; nextln: br %r14
function %store_i8_off(i8, i64) {
block0(v0: i8, v1: i64):
store.i8 little v0, v1+4096
return
}
; check: stcy %r2, 4096(%r3)
; nextln: br %r14
function %store_imm_i8(i64) {
block0(v0: i64):
v1 = iconst.i8 123
store.i8 little v1, v0
return
}
; check: mvi 0(%r2), 123
; nextln: br %r14
function %store_imm_i8_off(i64) {
block0(v0: i64):
v1 = iconst.i8 123
store.i8 little v1, v0+4096
return
}
; check: mviy 4096(%r2), 123
; nextln: br %r14

296
cranelift/filetests/filetests/isa/s390x/store.clif

@ -0,0 +1,296 @@
test compile
target s390x
function %store_i64(i64, i64) {
block0(v0: i64, v1: i64):
store.i64 v0, v1
return
}
; check: stg %r2, 0(%r3)
; nextln: br %r14
function %store_i64_sym(i64) {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
store.i64 v0, v1
return
}
; check: stgrl %r2, %sym + 0
; nextln: br %r14
function %store_imm_i64(i64) {
block0(v0: i64):
v1 = iconst.i64 12345
store.i64 v1, v0
return
}
; check: mvghi 0(%r2), 12345
; nextln: br %r14
function %istore8_i64(i64, i64) {
block0(v0: i64, v1: i64):
istore8.i64 v0, v1
return
}
; check: stc %r2, 0(%r3)
; nextln: br %r14
function %istore8_imm_i64(i64) {
block0(v0: i64):
v1 = iconst.i64 123
istore8.i64 v1, v0
return
}
; check: mvi 0(%r2), 123
; nextln: br %r14
function %istore16_i64(i64, i64) {
block0(v0: i64, v1: i64):
istore16.i64 v0, v1
return
}
; check: sth %r2, 0(%r3)
; nextln: br %r14
function %istore16_i64_sym(i64) {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
istore16.i64 v0, v1
return
}
; check: sthrl %r2, %sym + 0
; nextln: br %r14
function %istore16_imm_i64(i64) {
block0(v0: i64):
v1 = iconst.i64 12345
istore16.i64 v1, v0
return
}
; check: mvhhi 0(%r2), 12345
; nextln: br %r14
function %istore32_i64(i64, i64) {
block0(v0: i64, v1: i64):
istore32.i64 v0, v1
return
}
; check: st %r2, 0(%r3)
; nextln: br %r14
function %istore32_i64_sym(i64) {
gv0 = symbol colocated %sym
block0(v0: i64):
v1 = symbol_value.i64 gv0
istore32.i64 v0, v1
return
}
; check: strl %r2, %sym + 0
; nextln: br %r14
function %istore32_imm_i64(i64) {
block0(v0: i64):
v1 = iconst.i64 12345
istore32.i64 v1, v0
return
}
; check: mvhi 0(%r2), 12345
; nextln: br %r14
function %store_i32(i32, i64) {
block0(v0: i32, v1: i64):
store.i32 v0, v1
return
}
; check: st %r2, 0(%r3)
; nextln: br %r14
function %store_i32_sym(i32) {
gv0 = symbol colocated %sym
block0(v0: i32):
v1 = symbol_value.i64 gv0
store.i32 v0, v1
return
}
; check: strl %r2, %sym + 0
; nextln: br %r14
function %store_i32_off(i32, i64) {
block0(v0: i32, v1: i64):
store.i32 v0, v1+4096
return
}
; check: sty %r2, 4096(%r3)
; nextln: br %r14
function %store_imm_i32(i64) {
block0(v0: i64):
v1 = iconst.i32 12345
store.i32 v1, v0
return
}
; check: mvhi 0(%r2), 12345
; nextln: br %r14
function %istore8_i32(i32, i64) {
block0(v0: i32, v1: i64):
istore8.i32 v0, v1
return
}
; check: stc %r2, 0(%r3)
; nextln: br %r14
function %istore8_imm_i32(i64) {
block0(v0: i64):
v1 = iconst.i32 123
istore8.i32 v1, v0
return
}
; check: mvi 0(%r2), 123
; nextln: br %r14
function %istore16_i32(i32, i64) {
block0(v0: i32, v1: i64):
istore16.i32 v0, v1
return
}
; check: sth %r2, 0(%r3)
; nextln: br %r14
function %istore16_i32_sym(i32) {
gv0 = symbol colocated %sym
block0(v0: i32):
v1 = symbol_value.i64 gv0
istore16.i32 v0, v1
return
}
; check: sthrl %r2, %sym + 0
; nextln: br %r14
function %istore16_imm_i32(i64) {
block0(v0: i64):
v1 = iconst.i32 12345
istore16.i32 v1, v0
return
}
; check: mvhhi 0(%r2), 12345
; nextln: br %r14
function %store_i16(i16, i64) {
block0(v0: i16, v1: i64):
store.i16 v0, v1
return
}
; check: sth %r2, 0(%r3)
; nextln: br %r14
function %store_i16_sym(i16) {
gv0 = symbol colocated %sym
block0(v0: i16):
v1 = symbol_value.i64 gv0
store.i16 v0, v1
return
}
; check: sthrl %r2, %sym + 0
; nextln: br %r14
function %store_i16_off(i16, i64) {
block0(v0: i16, v1: i64):
store.i16 v0, v1+4096
return
}
; check: sthy %r2, 4096(%r3)
; nextln: br %r14
function %store_imm_i16(i64) {
block0(v0: i64):
v1 = iconst.i16 12345
store.i16 v1, v0
return
}
; check: mvhhi 0(%r2), 12345
; nextln: br %r14
function %istore8_i16(i16, i64) {
block0(v0: i16, v1: i64):
istore8.i16 v0, v1
return
}
; check: stc %r2, 0(%r3)
; nextln: br %r14
function %istore8_imm_i16(i64) {
block0(v0: i64):
v1 = iconst.i16 123
istore8.i16 v1, v0
return
}
; check: mvi 0(%r2), 123
; nextln: br %r14
function %store_i8(i8, i64) {
block0(v0: i8, v1: i64):
store.i8 v0, v1
return
}
; check: stc %r2, 0(%r3)
; nextln: br %r14
function %store_i8_off(i8, i64) {
block0(v0: i8, v1: i64):
store.i8 v0, v1+4096
return
}
; check: stcy %r2, 4096(%r3)
; nextln: br %r14
function %store_imm_i8(i64) {
block0(v0: i64):
v1 = iconst.i8 123
store.i8 v1, v0
return
}
; check: mvi 0(%r2), 123
; nextln: br %r14
function %store_imm_i8_off(i64) {
block0(v0: i64):
v1 = iconst.i8 123
store.i8 v1, v0+4096
return
}
; check: mviy 4096(%r2), 123
; nextln: br %r14

54
cranelift/filetests/filetests/isa/s390x/symbols.clif

@ -0,0 +1,54 @@
test compile
target s390x
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SYMBOL_VALUE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %symbol_value() -> i64 {
gv0 = symbol %my_global
block0:
v0 = symbol_value.i64 gv0
return v0
}
; check: bras %r1, 12 ; data %my_global + 0 ; lg %r2, 0(%r1)
; nextln: br %r14
function %symbol_value_colocated() -> i64 {
gv0 = symbol colocated %my_global_colo
block0:
v0 = symbol_value.i64 gv0
return v0
}
; check: larl %r2, %my_global_colo + 0
; nextln: br %r14
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FUNC_ADDR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %func_addr() -> i64 {
fn0 = %my_func(i64) -> i64
block0:
v0 = func_addr.i64 fn0
return v0
}
; check: bras %r1, 12 ; data %my_func + 0 ; lg %r2, 0(%r1)
; nextln: br %r14
function %func_addr_colocated() -> i64 {
fn0 = colocated %my_func_colo(i64) -> i64
block0:
v0 = func_addr.i64 fn0
return v0
}
; check: larl %r2, %my_func_colo + 0
; nextln: br %r14

91
cranelift/filetests/filetests/isa/s390x/traps.clif

@ -0,0 +1,91 @@
test compile
target s390x
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TRAP/RESUMABLE_TRAP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %trap() {
block0:
trap user0
}
; check: trap
function %resumable_trap() {
block0:
trap user0
}
; check: trap
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TRAPZ
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %trapz(i64) {
block0(v0: i64):
v1 = iconst.i64 42
v2 = icmp eq v0, v1
trapz v2, user0
return
}
; FIXME: Does not use TrapIf internally as trapz is expanded.
; check: Block 0
; check: clgfi %r2, 42
; nextln: jge label1 ; jg label2
; check: Block 1:
; check: br %r14
; check: Block 2:
; check: trap
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TRAPNZ/RESUMABLE_TRAPNZ
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %trapnz(i64) {
block0(v0: i64):
v1 = iconst.i64 42
v2 = icmp eq v0, v1
trapnz v2, user0
return
}
; FIXME: Does not use TrapIf internally as trapnz is expanded.
; check: Block 0
; check: clgfi %r2, 42
; nextln: jgne label1 ; jg label2
; check: Block 1:
; check: br %r14
; check: Block 2:
; check: trap
function %resumable_trapnz(i64) {
block0(v0: i64):
v1 = iconst.i64 42
v2 = icmp eq v0, v1
trapnz v2, user0
return
}
; FIXME: Does not use TrapIf internally as resumable_trapnz is expanded.
; check: Block 0
; check: clgfi %r2, 42
; nextln: jgne label1 ; jg label2
; check: Block 1:
; check: br %r14
; check: Block 2:
; check: trap
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DEBUGTRAP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
function %h() {
block0:
debugtrap
return
}
; check: debugtrap
Loading…
Cancel
Save