From 33cb5834345cefe98a55500c3221514ece9e2c1d Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Wed, 9 Apr 2025 14:10:02 +0200 Subject: [PATCH] s390x: Add try_call / try_call_indirect support Following-up on https://github.com/bytecodealliance/wasmtime/pull/10510 this adds the corresponding support to the s390x target. Due to the separate ABI implementation, this currently needs to duplicate some of the underlying logic. I hope to be able to refactor ABI handling in the future to be able to share more of that code across all targets. --- cranelift/codegen/src/isa/s390x/abi.rs | 63 ++++- cranelift/codegen/src/isa/s390x/inst.isle | 21 +- cranelift/codegen/src/isa/s390x/inst/emit.rs | 17 ++ cranelift/codegen/src/isa/s390x/inst/mod.rs | 17 +- cranelift/codegen/src/isa/s390x/inst/regs.rs | 8 +- cranelift/codegen/src/isa/s390x/lower.isle | 24 ++ cranelift/codegen/src/isa/s390x/lower/isle.rs | 222 ++++++++++------ .../filetests/isa/s390x/exceptions.clif | 239 ++++++++++++++++++ 8 files changed, 527 insertions(+), 84 deletions(-) create mode 100644 cranelift/filetests/filetests/isa/s390x/exceptions.clif diff --git a/cranelift/codegen/src/isa/s390x/abi.rs b/cranelift/codegen/src/isa/s390x/abi.rs index 7dd2c53c7c66..27076655608e 100644 --- a/cranelift/codegen/src/isa/s390x/abi.rs +++ b/cranelift/codegen/src/isa/s390x/abi.rs @@ -906,8 +906,8 @@ impl ABIMachineSpec for S390xMachineDeps { call_conv_of_callee: isa::CallConv, is_exception: bool, ) -> PRegSet { - assert!(!is_exception); match call_conv_of_callee { + _ if is_exception => ALL_CLOBBERS, isa::CallConv::Tail => TAIL_CLOBBERS, _ => SYSV_CLOBBERS, } @@ -1009,6 +1009,14 @@ impl ABIMachineSpec for S390xMachineDeps { fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable { panic!("Should not be called"); } + + fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] { + const PAYLOAD_REGS: &'static [Reg] = &[gpr(6), gpr(7)]; + match call_conv { + isa::CallConv::SystemV | isa::CallConv::Tail => PAYLOAD_REGS, + _ => &[], + } + } } impl S390xMachineDeps { @@ -1378,6 +1386,59 @@ const fn tail_clobbers() -> PRegSet { } const TAIL_CLOBBERS: PRegSet = tail_clobbers(); +const fn all_clobbers() -> PRegSet { + PRegSet::empty() + .with(gpr_preg(0)) + .with(gpr_preg(1)) + .with(gpr_preg(2)) + .with(gpr_preg(3)) + .with(gpr_preg(4)) + .with(gpr_preg(5)) + .with(gpr_preg(6)) + .with(gpr_preg(7)) + .with(gpr_preg(8)) + .with(gpr_preg(9)) + .with(gpr_preg(10)) + .with(gpr_preg(11)) + .with(gpr_preg(12)) + .with(gpr_preg(13)) + .with(gpr_preg(14)) + .with(gpr_preg(15)) + .with(vr_preg(0)) + .with(vr_preg(1)) + .with(vr_preg(2)) + .with(vr_preg(3)) + .with(vr_preg(4)) + .with(vr_preg(5)) + .with(vr_preg(6)) + .with(vr_preg(7)) + .with(vr_preg(8)) + .with(vr_preg(9)) + .with(vr_preg(10)) + .with(vr_preg(11)) + .with(vr_preg(12)) + .with(vr_preg(13)) + .with(vr_preg(14)) + .with(vr_preg(15)) + .with(vr_preg(16)) + .with(vr_preg(17)) + .with(vr_preg(18)) + .with(vr_preg(19)) + .with(vr_preg(20)) + .with(vr_preg(21)) + .with(vr_preg(22)) + .with(vr_preg(23)) + .with(vr_preg(24)) + .with(vr_preg(25)) + .with(vr_preg(26)) + .with(vr_preg(27)) + .with(vr_preg(28)) + .with(vr_preg(29)) + .with(vr_preg(30)) + .with(vr_preg(31)) +} +const ALL_CLOBBERS: PRegSet = all_clobbers(); + fn sysv_create_machine_env() -> MachineEnv { MachineEnv { preferred_regs_by_class: [ diff --git a/cranelift/codegen/src/isa/s390x/inst.isle b/cranelift/codegen/src/isa/s390x/inst.isle index e0f56809a984..0badf6785494 100644 --- a/cranelift/codegen/src/isa/s390x/inst.isle +++ b/cranelift/codegen/src/isa/s390x/inst.isle @@ -2669,10 +2669,9 @@ dst)) ;; Helper for emitting `MInst.Call` instructions. -(decl call_impl (WritableReg CallSiteInfo) InstOutput) -(rule (call_impl reg (call_site_info info output)) - (let ((_ Unit (emit (MInst.Call reg info)))) - output)) +(decl call_impl (WritableReg BoxCallInfo) SideEffectNoResult) +(rule (call_impl reg info) + (SideEffectNoResult.Inst (MInst.Call reg info))) ;; Helper for emitting `MInst.ReturnCall` instructions. (decl return_call_impl (BoxReturnCallInfo) SideEffectNoResult) @@ -3478,6 +3477,9 @@ (decl abi_call_site_info (Sig CallInstDest CallArgList) CallSiteInfo) (extern constructor abi_call_site_info abi_call_site_info) +(decl abi_try_call_info (Sig CallInstDest CallArgList ExceptionTable MachLabelSlice) BoxCallInfo) +(extern constructor abi_try_call_info abi_try_call_info) + (decl abi_return_call_info (Sig CallInstDest CallArgList) BoxReturnCallInfo) (extern constructor abi_return_call_info abi_return_call_info) @@ -3495,13 +3497,20 @@ (decl abi_call (Sig CallInstDest CallArgList) InstOutput) (rule (abi_call abi dest uses) - (let ((info CallSiteInfo (abi_call_site_info abi dest uses))) - (call_impl (writable_link_reg) info))) + (abi_call_impl (abi_call_site_info abi dest uses))) +(decl abi_call_impl (CallSiteInfo) InstOutput) +(rule (abi_call_impl (call_site_info info output)) + (let ((_ Unit (emit_side_effect (call_impl (writable_link_reg) info)))) + output)) (decl abi_return_call (Sig CallInstDest CallArgList) SideEffectNoResult) (rule (abi_return_call abi dest uses) (return_call_impl (abi_return_call_info abi dest uses))) +(decl abi_try_call (Sig CallInstDest CallArgList ExceptionTable MachLabelSlice) SideEffectNoResult) +(rule (abi_try_call abi dest uses et targets) + (call_impl (writable_link_reg) (abi_try_call_info abi dest uses et targets))) + (decl abi_lane_order (Sig) LaneOrder) (extern constructor abi_lane_order abi_lane_order) diff --git a/cranelift/codegen/src/isa/s390x/inst/emit.rs b/cranelift/codegen/src/isa/s390x/inst/emit.rs index a564d2f2bee9..aa13cadeb397 100644 --- a/cranelift/codegen/src/isa/s390x/inst/emit.rs +++ b/cranelift/codegen/src/isa/s390x/inst/emit.rs @@ -3218,11 +3218,28 @@ impl Inst { put(sink, enc); sink.add_call_site(); + // Add exception info, if any, at this point (which will + // be the return address on stack). + if let Some(try_call) = info.try_call_info.as_ref() { + for &(tag, label) in &try_call.exception_dests { + sink.add_exception_handler(tag, label); + } + } + state.nominal_sp_offset -= info.callee_pop_size; for inst in S390xMachineDeps::gen_retval_loads(info) { inst.emit(sink, emit_info, state); } + + // If this is a try-call, jump to the continuation + // (normal-return) block. + if let Some(try_call) = info.try_call_info.as_ref() { + let jmp = Inst::Jump { + dest: try_call.continuation, + }; + jmp.emit(sink, emit_info, state); + } } &Inst::ReturnCall { ref info } => { let (epilogue_insts, temp_dest) = S390xMachineDeps::gen_tail_epilogue( diff --git a/cranelift/codegen/src/isa/s390x/inst/mod.rs b/cranelift/codegen/src/isa/s390x/inst/mod.rs index c4d71dea30e5..70ab352c8d0c 100644 --- a/cranelift/codegen/src/isa/s390x/inst/mod.rs +++ b/cranelift/codegen/src/isa/s390x/inst/mod.rs @@ -1076,6 +1076,7 @@ impl MachInst for Inst { &Inst::CondBr { .. } => MachTerminator::Branch, &Inst::IndirectBr { .. } => MachTerminator::Branch, &Inst::JTSequence { .. } => MachTerminator::Branch, + &Inst::Call { ref info, .. } if info.try_call_info.is_some() => MachTerminator::Branch, _ => MachTerminator::None, } } @@ -3119,18 +3120,30 @@ impl Inst { if !retval_loads.is_empty() { retval_loads = " ; ".to_string() + &retval_loads; } + let try_call = if let Some(try_call_info) = &info.try_call_info { + let dests = try_call_info + .exception_dests + .iter() + .map(|(tag, label)| format!("{tag:?}: {label:?}")) + .collect::>() + .join(", "); + format!("; jg {:?}; catch [{dests}]", try_call_info.continuation) + } else { + "".to_string() + }; let callee_pop_size = if info.callee_pop_size > 0 { format!(" ; callee_pop_size {}", info.callee_pop_size) } else { "".to_string() }; format!( - "{} {}, {}{}{}", + "{} {}, {}{}{}{}", opcode, show_reg(link), dest, callee_pop_size, - retval_loads + retval_loads, + try_call ) } &Inst::ReturnCall { ref info } => { diff --git a/cranelift/codegen/src/isa/s390x/inst/regs.rs b/cranelift/codegen/src/isa/s390x/inst/regs.rs index cbdc628cc555..9c9327d4094b 100644 --- a/cranelift/codegen/src/isa/s390x/inst/regs.rs +++ b/cranelift/codegen/src/isa/s390x/inst/regs.rs @@ -10,8 +10,8 @@ use crate::machinst::*; // Registers, the Universe thereof, and printing /// Get a reference to a GPR (integer register). -pub fn gpr(num: u8) -> Reg { - Reg::from(gpr_preg(num)) +pub const fn gpr(num: u8) -> Reg { + Reg::from_real_reg(gpr_preg(num)) } pub(crate) const fn gpr_preg(num: u8) -> PReg { @@ -25,8 +25,8 @@ pub fn writable_gpr(num: u8) -> Writable { } /// Get a reference to a VR (vector register). -pub fn vr(num: u8) -> Reg { - Reg::from(vr_preg(num)) +pub const fn vr(num: u8) -> Reg { + Reg::from_real_reg(vr_preg(num)) } pub(crate) const fn vr_preg(num: u8) -> PReg { diff --git a/cranelift/codegen/src/isa/s390x/lower.isle b/cranelift/codegen/src/isa/s390x/lower.isle index 3d85be99fc59..c61c4d6f003c 100644 --- a/cranelift/codegen/src/isa/s390x/lower.isle +++ b/cranelift/codegen/src/isa/s390x/lower.isle @@ -3926,6 +3926,30 @@ (args_builder_finish uses))) +;;;; Rules for `try_call` and `try_call_indirect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Direct call to an in-range function. +(rule 1 (lower_branch (try_call (func_ref_data sig_ref name (reloc_distance_near)) args et) targets) + (let ((abi Sig (abi_sig sig_ref)) + (uses CallArgList (lower_call_args abi (range 0 (abi_num_args abi)) args))) + (emit_side_effect (abi_try_call abi (CallInstDest.Direct name) uses et targets)))) + +;; Direct call to an out-of-range function (implicitly via pointer). +(rule (lower_branch (try_call (func_ref_data sig_ref name _) args et) targets) + (let ((abi Sig (abi_sig sig_ref)) + (uses CallArgList (lower_call_args abi (range 0 (abi_num_args abi)) args)) + (target Reg (load_symbol_reloc (SymbolReloc.Absolute name 0)))) + (emit_side_effect (abi_try_call abi (CallInstDest.Indirect target) uses et targets)))) + +;; Indirect call. +(rule (lower_branch (try_call_indirect ptr args et) targets) + (if-let (exception_sig sig_ref) et) + (let ((abi Sig (abi_sig sig_ref)) + (target Reg (put_in_reg ptr)) + (uses CallArgList (lower_call_args abi (range 0 (abi_num_args abi)) args))) + (emit_side_effect (abi_try_call abi (CallInstDest.Indirect target) uses et targets)))) + + ;;;; Common helpers for argument lowering ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Lower function arguments (part 1): prepare buffer copies. diff --git a/cranelift/codegen/src/isa/s390x/lower/isle.rs b/cranelift/codegen/src/isa/s390x/lower/isle.rs index 842d514af2d4..462ca46ce09f 100644 --- a/cranelift/codegen/src/isa/s390x/lower/isle.rs +++ b/cranelift/codegen/src/isa/s390x/lower/isle.rs @@ -12,7 +12,7 @@ use crate::isa::s390x::inst::{ }; use crate::isa::s390x::S390xBackend; use crate::machinst::{isle::*, RetLocation}; -use crate::machinst::{CallInfo, MachLabel, Reg, StackAMode}; +use crate::machinst::{CallInfo, MachLabel, Reg, StackAMode, TryCallInfo}; use crate::{ ir::{ condcodes::*, immediates::*, types::*, ArgumentExtension, ArgumentPurpose, AtomicRmwOp, @@ -226,78 +226,24 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> { &MemArg::NominalSPOffset { off } => off, _ => unreachable!(), }; - // Helper routine to compute the type after argument extension. - let ext_ty = |ty, extension| match (ty, extension) { - (ty, ArgumentExtension::None) => ty, - (I8, _) => I64, - (I16, _) => I64, - (I32, _) => I64, - _ => ty, - }; - // Allocate writable registers for all retval regs, except for StructRet args. - let mut defs: CallRetList = smallvec![]; - let mut outputs = InstOutput::new(); - for i in 0..self.lower_ctx.sigs().num_rets(abi) { - if let &ABIArg::Slots { - ref slots, purpose, .. - } = &self.lower_ctx.sigs().get_ret(abi, i) - { - if purpose == ArgumentPurpose::StructReturn { - continue; - } - // Our ABI always uses a single slot. - debug_assert_eq!(slots.len(), 1); - match &slots[0] { - &ABIArgSlot::Reg { reg, ty, extension } => { - let ty = ext_ty(ty, extension); - let into_reg = self.lower_ctx.alloc_tmp(ty).only_reg().unwrap(); - defs.push(CallRetPair { - vreg: into_reg, - location: RetLocation::Reg(reg.into(), ty), - }); - outputs.push(ValueRegs::one(into_reg.to_reg())); - } - &ABIArgSlot::Stack { - offset, - ty, - extension, - } => { - let ty = ext_ty(ty, extension); - let into_reg = self.lower_ctx.alloc_tmp(ty).only_reg().unwrap(); - let amode = StackAMode::OutgoingArg(offset + ret_area_base); - defs.push(CallRetPair { - vreg: into_reg, - location: RetLocation::Stack(amode, ty), - }); - outputs.push(ValueRegs::one(into_reg.to_reg())); - } - } - } - } + self.abi_common_call_site_info(abi, dest, uses, ret_area_base, None) + } - let sig_data = &self.lower_ctx.sigs()[abi]; - // Get clobbers: all caller-saves. These may include return value - // regs, which we will remove from the clobber set later. - let clobbers = S390xMachineDeps::get_regs_clobbered_by_call( - sig_data.call_conv(), - /* is_exception = */ false, - ); - let callee_pop_size = if sig_data.call_conv() == CallConv::Tail { - sig_data.sized_stack_arg_space() as u32 - } else { - 0 + fn abi_try_call_info( + &mut self, + abi: Sig, + dest: &CallInstDest, + uses: &CallArgList, + et: ExceptionTable, + targets: &MachLabelSlice, + ) -> BoxCallInfo { + // Determine return buffer address. + let ret_area_base = match &self.abi_call_stack_rets(abi) { + &MemArg::NominalSPOffset { off } => off, + _ => unreachable!(), }; - let info = Box::new(CallInfo { - dest: dest.clone(), - uses: uses.clone(), - defs: defs, - clobbers, - callee_pop_size, - caller_conv: self.lower_ctx.abi().call_conv(self.lower_ctx.sigs()), - callee_conv: self.lower_ctx.sigs()[abi].call_conv(), - try_call_info: None, - }); - (info, outputs) + self.abi_common_call_site_info(abi, dest, uses, ret_area_base, Some((et, targets))) + .0 } fn abi_return_call_info( @@ -984,6 +930,140 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> { } } +impl IsleContext<'_, '_, MInst, S390xBackend> { + fn abi_common_call_site_info( + &mut self, + abi: Sig, + dest: &CallInstDest, + uses: &CallArgList, + ret_area_base: i64, + try_call_info: Option<(ExceptionTable, &MachLabelSlice)>, + ) -> CallSiteInfo { + let caller_conv = self.lower_ctx.abi().call_conv(self.lower_ctx.sigs()); + let callee_conv = self.lower_ctx.sigs()[abi].call_conv(); + // Helper routine to compute the type after argument extension. + let ext_ty = |ty, extension| match (ty, extension) { + (ty, ArgumentExtension::None) => ty, + (I8, _) => I64, + (I16, _) => I64, + (I32, _) => I64, + _ => ty, + }; + // Allocate writable registers for all retval regs, except for StructRet args. + let mut defs: CallRetList = smallvec![]; + let mut outputs = InstOutput::new(); + for i in 0..self.lower_ctx.sigs().num_rets(abi) { + if let &ABIArg::Slots { + ref slots, purpose, .. + } = &self.lower_ctx.sigs().get_ret(abi, i) + { + if purpose == ArgumentPurpose::StructReturn { + continue; + } + // Our ABI always uses a single slot. + debug_assert_eq!(slots.len(), 1); + match &slots[0] { + &ABIArgSlot::Reg { reg, ty, extension } => { + let ty = ext_ty(ty, extension); + let into_reg = self.lower_ctx.alloc_tmp(ty).only_reg().unwrap(); + defs.push(CallRetPair { + vreg: into_reg, + location: RetLocation::Reg(reg.into(), ty), + }); + outputs.push(ValueRegs::one(into_reg.to_reg())); + } + &ABIArgSlot::Stack { + offset, + ty, + extension, + } => { + let ty = ext_ty(ty, extension); + let into_reg = self.lower_ctx.alloc_tmp(ty).only_reg().unwrap(); + let amode = StackAMode::OutgoingArg(offset + ret_area_base); + defs.push(CallRetPair { + vreg: into_reg, + location: RetLocation::Stack(amode, ty), + }); + outputs.push(ValueRegs::one(into_reg.to_reg())); + } + } + } + } + + // Handle exceptions for try_call, in particular set up the exception + // payload registers. This matches the corresponding code in emit_call + // and gen_call_common. + let try_call_info = try_call_info.map(|(et, labels)| { + let exception_dests = self.lower_ctx.dfg().exception_tables[et] + .catches() + .map(|(tag, _)| tag.into()) + .zip(labels.iter().cloned()) + .collect::>() + .into_boxed_slice(); + + let pregs = S390xMachineDeps::exception_payload_regs(callee_conv); + for (i, &preg) in pregs.iter().enumerate() { + let vreg = self + .lower_ctx + .try_call_exception_defs(self.lower_ctx.cur_inst())[i]; + if let Some(existing) = defs.iter().find(|def| match def.location { + RetLocation::Reg(r, _) => r == preg, + _ => false, + }) { + self.lower_ctx + .vregs_mut() + .set_vreg_alias(vreg.to_reg(), existing.vreg.to_reg()); + } else { + defs.push(CallRetPair { + vreg, + location: RetLocation::Reg(preg, I64), + }); + } + } + + TryCallInfo { + continuation: *labels.last().unwrap(), + exception_dests, + } + }); + if try_call_info.is_some() { + for i in 0..outputs.len() { + let result_regs = outputs[i]; + let def_regs = self + .lower_ctx + .try_call_return_defs(self.lower_ctx.cur_inst())[i]; + for (result_reg, def_reg) in result_regs.regs().iter().zip(def_regs.regs().iter()) { + self.lower_ctx + .vregs_mut() + .set_vreg_alias(def_reg.to_reg(), *result_reg); + } + } + } + + // Get clobbers: all caller-saves. These may include return value + // regs, which we will remove from the clobber set later. + let clobbers = + S390xMachineDeps::get_regs_clobbered_by_call(callee_conv, try_call_info.is_some()); + let callee_pop_size = if callee_conv == CallConv::Tail { + let sig_data = &self.lower_ctx.sigs()[abi]; + sig_data.sized_stack_arg_space() as u32 + } else { + 0 + }; + let info = Box::new(CallInfo { + dest: dest.clone(), + uses: uses.clone(), + defs: defs, + clobbers, + callee_pop_size, + caller_conv, + callee_conv, + try_call_info, + }); + (info, outputs) + } +} + /// Zero-extend the low `from_bits` bits of `value` to a full u64. #[inline] fn zero_extend_to_u64(value: u64, from_bits: u8) -> u64 { diff --git a/cranelift/filetests/filetests/isa/s390x/exceptions.clif b/cranelift/filetests/filetests/isa/s390x/exceptions.clif new file mode 100644 index 000000000000..723b1685b1fd --- /dev/null +++ b/cranelift/filetests/filetests/isa/s390x/exceptions.clif @@ -0,0 +1,239 @@ +test compile precise-output +target s390x + +function %f0(i32) -> i32, f32, f64 { + sig0 = (i32) -> f32 tail + fn0 = colocated %g(i32) -> f32 tail + + block0(v1: i32): + v2 = f64const 0x1.0 + try_call fn0(v1), sig0, block1(ret0, v2), [ default: block2(exn0) ] + + block1(v3: f32, v4: f64): + v5 = iconst.i32 1 + return v5, v3, v4 + + block2(v6: i64): + v7 = ireduce.i32 v6 + v8 = iadd_imm.i32 v7, 1 + v9 = f32const 0x0.0 + return v8, v9, v2 +} + +; VCode: +; stmg %r6, %r15, 48(%r15) +; aghi %r15, -240 +; std %f8, 176(%r15) +; std %f9, 184(%r15) +; std %f10, 192(%r15) +; std %f11, 200(%r15) +; std %f12, 208(%r15) +; std %f13, 216(%r15) +; std %f14, 224(%r15) +; std %f15, 232(%r15) +; block0: +; bras %r1, 12 ; data.f64 1 ; ld %f2, 0(%r1) +; vst %v2, 160(%r15) +; brasl %r14, %g; jg MachLabel(1); catch [None: MachLabel(2)] +; block1: +; lhi %r2, 1 +; vl %v2, 160(%r15) +; ld %f8, 176(%r15) +; ld %f9, 184(%r15) +; ld %f10, 192(%r15) +; ld %f11, 200(%r15) +; ld %f12, 208(%r15) +; ld %f13, 216(%r15) +; ld %f14, 224(%r15) +; ld %f15, 232(%r15) +; lmg %r6, %r15, 288(%r15) +; br %r14 +; block2: +; vl %v2, 160(%r15) +; ahik %r2, %r6, 1 +; bras %r1, 8 ; data.f32 0 ; le %f0, 0(%r1) +; ld %f8, 176(%r15) +; ld %f9, 184(%r15) +; ld %f10, 192(%r15) +; ld %f11, 200(%r15) +; ld %f12, 208(%r15) +; ld %f13, 216(%r15) +; ld %f14, 224(%r15) +; ld %f15, 232(%r15) +; lmg %r6, %r15, 288(%r15) +; br %r14 +; +; Disassembled: +; block0: ; offset 0x0 +; stmg %r6, %r15, 0x30(%r15) +; aghi %r15, -0xf0 +; std %f8, 0xb0(%r15) +; std %f9, 0xb8(%r15) +; std %f10, 0xc0(%r15) +; std %f11, 0xc8(%r15) +; std %f12, 0xd0(%r15) +; std %f13, 0xd8(%r15) +; std %f14, 0xe0(%r15) +; std %f15, 0xe8(%r15) +; block1: ; offset 0x2a +; bras %r1, 0x36 +; sur %f15, %f0 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; ld %f2, 0(%r1) +; vst %v2, 0xa0(%r15) +; brasl %r14, 0x40 ; reloc_external PLTRel32Dbl %g 2 +; block2: ; offset 0x46 +; lhi %r2, 1 +; vl %v2, 0xa0(%r15) +; ld %f8, 0xb0(%r15) +; ld %f9, 0xb8(%r15) +; ld %f10, 0xc0(%r15) +; ld %f11, 0xc8(%r15) +; ld %f12, 0xd0(%r15) +; ld %f13, 0xd8(%r15) +; ld %f14, 0xe0(%r15) +; ld %f15, 0xe8(%r15) +; lmg %r6, %r15, 0x120(%r15) +; br %r14 +; block3: ; offset 0x78 +; vl %v2, 0xa0(%r15) +; ahik %r2, %r6, 1 +; bras %r1, 0x8c +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; le %f0, 0(%r1) +; ld %f8, 0xb0(%r15) +; ld %f9, 0xb8(%r15) +; ld %f10, 0xc0(%r15) +; ld %f11, 0xc8(%r15) +; ld %f12, 0xd0(%r15) +; ld %f13, 0xd8(%r15) +; ld %f14, 0xe0(%r15) +; ld %f15, 0xe8(%r15) +; lmg %r6, %r15, 0x120(%r15) +; br %r14 + +function %f2(i32) -> i32, f32, f64 { + sig0 = (i32) -> f32 tail + fn0 = %g(i32) -> f32 tail + + block0(v1: i32): + v2 = f64const 0x1.0 + v10 = func_addr.i64 fn0 + try_call_indirect v10(v1), sig0, block1(ret0, v2), [ default: block2(exn0) ] + + block1(v3: f32, v4: f64): + v5 = iconst.i32 1 + return v5, v3, v4 + + block2(v6: i64): + v7 = ireduce.i32 v6 + v8 = iadd_imm.i32 v7, 1 + v9 = f32const 0x0.0 + return v8, v9, v2 +} + +; VCode: +; stmg %r6, %r15, 48(%r15) +; aghi %r15, -240 +; std %f8, 176(%r15) +; std %f9, 184(%r15) +; std %f10, 192(%r15) +; std %f11, 200(%r15) +; std %f12, 208(%r15) +; std %f13, 216(%r15) +; std %f14, 224(%r15) +; std %f15, 232(%r15) +; block0: +; bras %r1, 12 ; data.f64 1 ; ld %f2, 0(%r1) +; vst %v2, 160(%r15) +; bras %r1, 12 ; data %g + 0 ; lg %r5, 0(%r1) +; basr %r14, %r5; jg MachLabel(1); catch [None: MachLabel(2)] +; block1: +; lhi %r2, 1 +; vl %v2, 160(%r15) +; ld %f8, 176(%r15) +; ld %f9, 184(%r15) +; ld %f10, 192(%r15) +; ld %f11, 200(%r15) +; ld %f12, 208(%r15) +; ld %f13, 216(%r15) +; ld %f14, 224(%r15) +; ld %f15, 232(%r15) +; lmg %r6, %r15, 288(%r15) +; br %r14 +; block2: +; vl %v2, 160(%r15) +; ahik %r2, %r6, 1 +; bras %r1, 8 ; data.f32 0 ; le %f0, 0(%r1) +; ld %f8, 176(%r15) +; ld %f9, 184(%r15) +; ld %f10, 192(%r15) +; ld %f11, 200(%r15) +; ld %f12, 208(%r15) +; ld %f13, 216(%r15) +; ld %f14, 224(%r15) +; ld %f15, 232(%r15) +; lmg %r6, %r15, 288(%r15) +; br %r14 +; +; Disassembled: +; block0: ; offset 0x0 +; stmg %r6, %r15, 0x30(%r15) +; aghi %r15, -0xf0 +; std %f8, 0xb0(%r15) +; std %f9, 0xb8(%r15) +; std %f10, 0xc0(%r15) +; std %f11, 0xc8(%r15) +; std %f12, 0xd0(%r15) +; std %f13, 0xd8(%r15) +; std %f14, 0xe0(%r15) +; std %f15, 0xe8(%r15) +; block1: ; offset 0x2a +; bras %r1, 0x36 +; sur %f15, %f0 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; ld %f2, 0(%r1) +; vst %v2, 0xa0(%r15) +; bras %r1, 0x4c +; .byte 0x00, 0x00 ; reloc_external Abs8 %g 0 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; lg %r5, 0(%r1) +; basr %r14, %r5 +; block2: ; offset 0x54 +; lhi %r2, 1 +; vl %v2, 0xa0(%r15) +; ld %f8, 0xb0(%r15) +; ld %f9, 0xb8(%r15) +; ld %f10, 0xc0(%r15) +; ld %f11, 0xc8(%r15) +; ld %f12, 0xd0(%r15) +; ld %f13, 0xd8(%r15) +; ld %f14, 0xe0(%r15) +; ld %f15, 0xe8(%r15) +; lmg %r6, %r15, 0x120(%r15) +; br %r14 +; block3: ; offset 0x86 +; vl %v2, 0xa0(%r15) +; ahik %r2, %r6, 1 +; bras %r1, 0x9a +; .byte 0x00, 0x00 +; .byte 0x00, 0x00 +; le %f0, 0(%r1) +; ld %f8, 0xb0(%r15) +; ld %f9, 0xb8(%r15) +; ld %f10, 0xc0(%r15) +; ld %f11, 0xc8(%r15) +; ld %f12, 0xd0(%r15) +; ld %f13, 0xd8(%r15) +; ld %f14, 0xe0(%r15) +; ld %f15, 0xe8(%r15) +; lmg %r6, %r15, 0x120(%r15) +; br %r14 +