Browse Source
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
43 changed files with 24276 additions and 2 deletions
@ -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, |
|||
) |
|||
} |
@ -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 = ¶ms[i]; |
|||
|
|||
// Validate "purpose".
|
|||
match ¶m.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 ® 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"), |
|||
} |
|||
} |
@ -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), |
|||
} |
|||
} |
|||
} |
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -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) |
|||
} |
|||
} |
File diff suppressed because it is too large
@ -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, |
|||
} |
|||
} |
@ -0,0 +1,2 @@ |
|||
#[cfg(feature = "unwind")] |
|||
pub(crate) mod systemv; |
@ -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 |
|||
} |
|||
} |
File diff suppressed because it is too large
@ -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[..]); |
|||
} |
|||
} |
@ -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")); |
File diff suppressed because it is too large
@ -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 |
@ -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 |
|||
|
@ -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 |
@ -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 |
@ -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 |
@ -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 |
|||
|
@ -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 |
|||
|
@ -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 |
|||
|
@ -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 |
@ -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 |
@ -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 |
|||
|
@ -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 |
|||
|
|||
|
@ -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 |
|||
|
@ -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 |
|||
|
@ -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 |
@ -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 |
@ -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 |
|||
} |
|||
|
@ -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 |
|||
|
@ -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 |
@ -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 |
|||
|
@ -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 |
|||
|
@ -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 |
|||
|
@ -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 |
@ -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…
Reference in new issue