mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-30 23:10:38 +00:00
af39bc8c49
This patch modifies SoftFloat library so that it can be configured in run-time in relation to the meaning of signaling NaN bit, while, at the same time, strictly preserving its behavior on all existing platforms. Background: In floating-point calculations, there is a need for denoting undefined or unrepresentable values. This is achieved by defining certain floating-point numerical values to be NaNs (which stands for "not a number"). For additional reasons, virtually all modern floating-point unit implementations use two kinds of NaNs: quiet and signaling. The binary representations of these two kinds of NaNs, as a rule, differ only in one bit (that bit is, traditionally, the first bit of mantissa). Up to 2008, standards for floating-point did not specify all details about binary representation of NaNs. More specifically, the meaning of the bit that is used for distinguishing between signaling and quiet NaNs was not strictly prescribed. (IEEE 754-2008 was the first floating-point standard that defined that meaning clearly, see [1], p. 35) As a result, different platforms took different approaches, and that presented considerable challenge for multi-platform emulators like QEMU. Mips platform represents the most complex case among QEMU-supported platforms regarding signaling NaN bit. Up to the Release 6 of Mips architecture, "1" in signaling NaN bit denoted signaling NaN, which is opposite to IEEE 754-2008 standard. From Release 6 on, Mips architecture adopted IEEE standard prescription, and "0" denotes signaling NaN. On top of that, Mips architecture for SIMD (also known as MSA, or vector instructions) also specifies signaling bit in accordance to IEEE standard. MSA unit can be implemented with both pre-Release 6 and Release 6 main processor units. QEMU uses SoftFloat library to implement various floating-point-related instructions on all platforms. The current QEMU implementation allows for defining meaning of signaling NaN bit during build time, and is implemented via preprocessor macro called SNAN_BIT_IS_ONE. On the other hand, the change in this patch enables SoftFloat library to be configured in run-time. This configuration is meant to occur during CPU initialization, at the moment when it is definitely known what desired behavior for particular CPU (or any additional FPUs) is. The change is implemented so that it is consistent with existing implementation of similar cases. This means that structure float_status is used for passing the information about desired signaling NaN bit on each invocation of SoftFloat functions. The additional field in float_status is called snan_bit_is_one, which supersedes macro SNAN_BIT_IS_ONE. IMPORTANT: This change is not meant to create any change in emulator behavior or functionality on any platform. It just provides the means for SoftFloat library to be used in a more flexible way - in other words, it will just prepare SoftFloat library for usage related to Mips platform and its specifics regarding signaling bit meaning, which is done in some of subsequent patches from this series. Further break down of changes: 1) Added field snan_bit_is_one to the structure float_status, and correspondent setter function set_snan_bit_is_one(). 2) Constants <float16|float32|float64|floatx80|float128>_default_nan (used both internally and externally) converted to functions <float16|float32|float64|floatx80|float128>_default_nan(float_status*). This is necessary since they are dependent on signaling bit meaning. At the same time, for the sake of code cleanup and simplicity, constants <floatx80|float128>_default_nan_<low|high> (used only internally within SoftFloat library) are removed, as not needed. 3) Added a float_status* argument to SoftFloat library functions XXX_is_quiet_nan(XXX a_), XXX_is_signaling_nan(XXX a_), XXX_maybe_silence_nan(XXX a_). This argument must be present in order to enable correct invocation of new version of functions XXX_default_nan(). (XXX is <float16|float32|float64|floatx80|float128> here) 4) Updated code for all platforms to reflect changes in SoftFloat library. This change is twofolds: it includes modifications of SoftFloat library functions invocations, and an addition of invocation of function set_snan_bit_is_one() during CPU initialization, with arguments that are appropriate for each particular platform. It was established that all platforms zero their main CPU data structures, so snan_bit_is_one(0) in appropriate places is not added, as it is not needed. [1] "IEEE Standard for Floating-Point Arithmetic", IEEE Computer Society, August 29, 2008. Signed-off-by: Thomas Schwinge <thomas@codesourcery.com> Signed-off-by: Maciej W. Rozycki <macro@codesourcery.com> Signed-off-by: Aleksandar Markovic <aleksandar.markovic@imgtec.com> Tested-by: Bastian Koppelmann <kbastian@mail.uni-paderborn.de> Reviewed-by: Leon Alrae <leon.alrae@imgtec.com> Tested-by: Leon Alrae <leon.alrae@imgtec.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [leon.alrae@imgtec.com: * cherry-picked 2 chunks from patch #2 to fix compilation warnings] Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
750 lines
22 KiB
C
750 lines
22 KiB
C
/*
|
|
* S/390 FPU helper routines
|
|
*
|
|
* Copyright (c) 2009 Ulrich Hecht
|
|
* Copyright (c) 2009 Alexander Graf
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "cpu.h"
|
|
#include "exec/exec-all.h"
|
|
#include "exec/cpu_ldst.h"
|
|
#include "exec/helper-proto.h"
|
|
|
|
/* #define DEBUG_HELPER */
|
|
#ifdef DEBUG_HELPER
|
|
#define HELPER_LOG(x...) qemu_log(x)
|
|
#else
|
|
#define HELPER_LOG(x...)
|
|
#endif
|
|
|
|
#define RET128(F) (env->retxl = F.low, F.high)
|
|
|
|
#define convert_bit(mask, from, to) \
|
|
(to < from \
|
|
? (mask / (from / to)) & to \
|
|
: (mask & from) * (to / from))
|
|
|
|
static void ieee_exception(CPUS390XState *env, uint32_t dxc, uintptr_t retaddr)
|
|
{
|
|
/* Install the DXC code. */
|
|
env->fpc = (env->fpc & ~0xff00) | (dxc << 8);
|
|
/* Trap. */
|
|
runtime_exception(env, PGM_DATA, retaddr);
|
|
}
|
|
|
|
/* Should be called after any operation that may raise IEEE exceptions. */
|
|
static void handle_exceptions(CPUS390XState *env, uintptr_t retaddr)
|
|
{
|
|
unsigned s390_exc, qemu_exc;
|
|
|
|
/* Get the exceptions raised by the current operation. Reset the
|
|
fpu_status contents so that the next operation has a clean slate. */
|
|
qemu_exc = env->fpu_status.float_exception_flags;
|
|
if (qemu_exc == 0) {
|
|
return;
|
|
}
|
|
env->fpu_status.float_exception_flags = 0;
|
|
|
|
/* Convert softfloat exception bits to s390 exception bits. */
|
|
s390_exc = 0;
|
|
s390_exc |= convert_bit(qemu_exc, float_flag_invalid, 0x80);
|
|
s390_exc |= convert_bit(qemu_exc, float_flag_divbyzero, 0x40);
|
|
s390_exc |= convert_bit(qemu_exc, float_flag_overflow, 0x20);
|
|
s390_exc |= convert_bit(qemu_exc, float_flag_underflow, 0x10);
|
|
s390_exc |= convert_bit(qemu_exc, float_flag_inexact, 0x08);
|
|
|
|
/* Install the exceptions that we raised. */
|
|
env->fpc |= s390_exc << 16;
|
|
|
|
/* Send signals for enabled exceptions. */
|
|
s390_exc &= env->fpc >> 24;
|
|
if (s390_exc) {
|
|
ieee_exception(env, s390_exc, retaddr);
|
|
}
|
|
}
|
|
|
|
static inline int float_comp_to_cc(CPUS390XState *env, int float_compare)
|
|
{
|
|
S390CPU *cpu = s390_env_get_cpu(env);
|
|
|
|
switch (float_compare) {
|
|
case float_relation_equal:
|
|
return 0;
|
|
case float_relation_less:
|
|
return 1;
|
|
case float_relation_greater:
|
|
return 2;
|
|
case float_relation_unordered:
|
|
return 3;
|
|
default:
|
|
cpu_abort(CPU(cpu), "unknown return value for float compare\n");
|
|
}
|
|
}
|
|
|
|
/* condition codes for unary FP ops */
|
|
uint32_t set_cc_nz_f32(float32 v)
|
|
{
|
|
if (float32_is_any_nan(v)) {
|
|
return 3;
|
|
} else if (float32_is_zero(v)) {
|
|
return 0;
|
|
} else if (float32_is_neg(v)) {
|
|
return 1;
|
|
} else {
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
uint32_t set_cc_nz_f64(float64 v)
|
|
{
|
|
if (float64_is_any_nan(v)) {
|
|
return 3;
|
|
} else if (float64_is_zero(v)) {
|
|
return 0;
|
|
} else if (float64_is_neg(v)) {
|
|
return 1;
|
|
} else {
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
uint32_t set_cc_nz_f128(float128 v)
|
|
{
|
|
if (float128_is_any_nan(v)) {
|
|
return 3;
|
|
} else if (float128_is_zero(v)) {
|
|
return 0;
|
|
} else if (float128_is_neg(v)) {
|
|
return 1;
|
|
} else {
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
/* 32-bit FP addition */
|
|
uint64_t HELPER(aeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
float32 ret = float32_add(f1, f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 64-bit FP addition */
|
|
uint64_t HELPER(adb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
float64 ret = float64_add(f1, f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 128-bit FP addition */
|
|
uint64_t HELPER(axb)(CPUS390XState *env, uint64_t ah, uint64_t al,
|
|
uint64_t bh, uint64_t bl)
|
|
{
|
|
float128 ret = float128_add(make_float128(ah, al),
|
|
make_float128(bh, bl),
|
|
&env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(ret);
|
|
}
|
|
|
|
/* 32-bit FP subtraction */
|
|
uint64_t HELPER(seb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
float32 ret = float32_sub(f1, f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 64-bit FP subtraction */
|
|
uint64_t HELPER(sdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
float64 ret = float64_sub(f1, f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 128-bit FP subtraction */
|
|
uint64_t HELPER(sxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
|
|
uint64_t bh, uint64_t bl)
|
|
{
|
|
float128 ret = float128_sub(make_float128(ah, al),
|
|
make_float128(bh, bl),
|
|
&env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(ret);
|
|
}
|
|
|
|
/* 32-bit FP division */
|
|
uint64_t HELPER(deb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
float32 ret = float32_div(f1, f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 64-bit FP division */
|
|
uint64_t HELPER(ddb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
float64 ret = float64_div(f1, f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 128-bit FP division */
|
|
uint64_t HELPER(dxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
|
|
uint64_t bh, uint64_t bl)
|
|
{
|
|
float128 ret = float128_div(make_float128(ah, al),
|
|
make_float128(bh, bl),
|
|
&env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(ret);
|
|
}
|
|
|
|
/* 32-bit FP multiplication */
|
|
uint64_t HELPER(meeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
float32 ret = float32_mul(f1, f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 64-bit FP multiplication */
|
|
uint64_t HELPER(mdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
float64 ret = float64_mul(f1, f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 64/32-bit FP multiplication */
|
|
uint64_t HELPER(mdeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
float64 ret = float32_to_float64(f2, &env->fpu_status);
|
|
ret = float64_mul(f1, ret, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 128-bit FP multiplication */
|
|
uint64_t HELPER(mxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
|
|
uint64_t bh, uint64_t bl)
|
|
{
|
|
float128 ret = float128_mul(make_float128(ah, al),
|
|
make_float128(bh, bl),
|
|
&env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(ret);
|
|
}
|
|
|
|
/* 128/64-bit FP multiplication */
|
|
uint64_t HELPER(mxdb)(CPUS390XState *env, uint64_t ah, uint64_t al,
|
|
uint64_t f2)
|
|
{
|
|
float128 ret = float64_to_float128(f2, &env->fpu_status);
|
|
ret = float128_mul(make_float128(ah, al), ret, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(ret);
|
|
}
|
|
|
|
/* convert 32-bit float to 64-bit float */
|
|
uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2)
|
|
{
|
|
float64 ret = float32_to_float64(f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return float64_maybe_silence_nan(ret, &env->fpu_status);
|
|
}
|
|
|
|
/* convert 128-bit float to 64-bit float */
|
|
uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al)
|
|
{
|
|
float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return float64_maybe_silence_nan(ret, &env->fpu_status);
|
|
}
|
|
|
|
/* convert 64-bit float to 128-bit float */
|
|
uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2)
|
|
{
|
|
float128 ret = float64_to_float128(f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(float128_maybe_silence_nan(ret, &env->fpu_status));
|
|
}
|
|
|
|
/* convert 32-bit float to 128-bit float */
|
|
uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2)
|
|
{
|
|
float128 ret = float32_to_float128(f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(float128_maybe_silence_nan(ret, &env->fpu_status));
|
|
}
|
|
|
|
/* convert 64-bit float to 32-bit float */
|
|
uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2)
|
|
{
|
|
float32 ret = float64_to_float32(f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return float32_maybe_silence_nan(ret, &env->fpu_status);
|
|
}
|
|
|
|
/* convert 128-bit float to 32-bit float */
|
|
uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al)
|
|
{
|
|
float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return float32_maybe_silence_nan(ret, &env->fpu_status);
|
|
}
|
|
|
|
/* 32-bit FP compare */
|
|
uint32_t HELPER(ceb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
int cmp = float32_compare_quiet(f1, f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return float_comp_to_cc(env, cmp);
|
|
}
|
|
|
|
/* 64-bit FP compare */
|
|
uint32_t HELPER(cdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
|
|
{
|
|
int cmp = float64_compare_quiet(f1, f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return float_comp_to_cc(env, cmp);
|
|
}
|
|
|
|
/* 128-bit FP compare */
|
|
uint32_t HELPER(cxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
|
|
uint64_t bh, uint64_t bl)
|
|
{
|
|
int cmp = float128_compare_quiet(make_float128(ah, al),
|
|
make_float128(bh, bl),
|
|
&env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return float_comp_to_cc(env, cmp);
|
|
}
|
|
|
|
static int swap_round_mode(CPUS390XState *env, int m3)
|
|
{
|
|
int ret = env->fpu_status.float_rounding_mode;
|
|
switch (m3) {
|
|
case 0:
|
|
/* current mode */
|
|
break;
|
|
case 1:
|
|
/* biased round no nearest */
|
|
case 4:
|
|
/* round to nearest */
|
|
set_float_rounding_mode(float_round_nearest_even, &env->fpu_status);
|
|
break;
|
|
case 5:
|
|
/* round to zero */
|
|
set_float_rounding_mode(float_round_to_zero, &env->fpu_status);
|
|
break;
|
|
case 6:
|
|
/* round to +inf */
|
|
set_float_rounding_mode(float_round_up, &env->fpu_status);
|
|
break;
|
|
case 7:
|
|
/* round to -inf */
|
|
set_float_rounding_mode(float_round_down, &env->fpu_status);
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* convert 64-bit int to 32-bit float */
|
|
uint64_t HELPER(cegb)(CPUS390XState *env, int64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float32 ret = int64_to_float32(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 64-bit int to 64-bit float */
|
|
uint64_t HELPER(cdgb)(CPUS390XState *env, int64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float64 ret = int64_to_float64(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 64-bit int to 128-bit float */
|
|
uint64_t HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float128 ret = int64_to_float128(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(ret);
|
|
}
|
|
|
|
/* convert 64-bit uint to 32-bit float */
|
|
uint64_t HELPER(celgb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float32 ret = uint64_to_float32(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 64-bit uint to 64-bit float */
|
|
uint64_t HELPER(cdlgb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float64 ret = uint64_to_float64(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 64-bit uint to 128-bit float */
|
|
uint64_t HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float128 ret = uint64_to_float128(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(ret);
|
|
}
|
|
|
|
/* convert 32-bit float to 64-bit int */
|
|
uint64_t HELPER(cgeb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
int64_t ret = float32_to_int64(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 64-bit float to 64-bit int */
|
|
uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
int64_t ret = float64_to_int64(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 128-bit float to 64-bit int */
|
|
uint64_t HELPER(cgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float128 v2 = make_float128(h, l);
|
|
int64_t ret = float128_to_int64(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 32-bit float to 32-bit int */
|
|
uint64_t HELPER(cfeb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
int32_t ret = float32_to_int32(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 64-bit float to 32-bit int */
|
|
uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
int32_t ret = float64_to_int32(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 128-bit float to 32-bit int */
|
|
uint64_t HELPER(cfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float128 v2 = make_float128(h, l);
|
|
int32_t ret = float128_to_int32(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 32-bit float to 64-bit uint */
|
|
uint64_t HELPER(clgeb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
uint64_t ret;
|
|
v2 = float32_to_float64(v2, &env->fpu_status);
|
|
ret = float64_to_uint64(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 64-bit float to 64-bit uint */
|
|
uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
uint64_t ret = float64_to_uint64(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 128-bit float to 64-bit uint */
|
|
uint64_t HELPER(clgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float128 v2 = make_float128(h, l);
|
|
/* ??? Not 100% correct. */
|
|
uint64_t ret = float128_to_int64(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 32-bit float to 32-bit uint */
|
|
uint64_t HELPER(clfeb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
uint32_t ret = float32_to_uint32(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 64-bit float to 32-bit uint */
|
|
uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
uint32_t ret = float64_to_uint32(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* convert 128-bit float to 32-bit uint */
|
|
uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float128 v2 = make_float128(h, l);
|
|
/* Not 100% correct. */
|
|
uint32_t ret = float128_to_int64(v2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* round to integer 32-bit */
|
|
uint64_t HELPER(fieb)(CPUS390XState *env, uint64_t f2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float32 ret = float32_round_to_int(f2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* round to integer 64-bit */
|
|
uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float64 ret = float64_round_to_int(f2, &env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* round to integer 128-bit */
|
|
uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint32_t m3)
|
|
{
|
|
int hold = swap_round_mode(env, m3);
|
|
float128 ret = float128_round_to_int(make_float128(ah, al),
|
|
&env->fpu_status);
|
|
set_float_rounding_mode(hold, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(ret);
|
|
}
|
|
|
|
/* 32-bit FP multiply and add */
|
|
uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1,
|
|
uint64_t f2, uint64_t f3)
|
|
{
|
|
float32 ret = float32_muladd(f2, f3, f1, 0, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 64-bit FP multiply and add */
|
|
uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1,
|
|
uint64_t f2, uint64_t f3)
|
|
{
|
|
float64 ret = float64_muladd(f2, f3, f1, 0, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 32-bit FP multiply and subtract */
|
|
uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1,
|
|
uint64_t f2, uint64_t f3)
|
|
{
|
|
float32 ret = float32_muladd(f2, f3, f1, float_muladd_negate_c,
|
|
&env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* 64-bit FP multiply and subtract */
|
|
uint64_t HELPER(msdb)(CPUS390XState *env, uint64_t f1,
|
|
uint64_t f2, uint64_t f3)
|
|
{
|
|
float64 ret = float64_muladd(f2, f3, f1, float_muladd_negate_c,
|
|
&env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* test data class 32-bit */
|
|
uint32_t HELPER(tceb)(CPUS390XState *env, uint64_t f1, uint64_t m2)
|
|
{
|
|
float32 v1 = f1;
|
|
int neg = float32_is_neg(v1);
|
|
uint32_t cc = 0;
|
|
|
|
if ((float32_is_zero(v1) && (m2 & (1 << (11-neg)))) ||
|
|
(float32_is_infinity(v1) && (m2 & (1 << (5-neg)))) ||
|
|
(float32_is_any_nan(v1) && (m2 & (1 << (3-neg)))) ||
|
|
(float32_is_signaling_nan(v1, &env->fpu_status) &&
|
|
(m2 & (1 << (1-neg))))) {
|
|
cc = 1;
|
|
} else if (m2 & (1 << (9-neg))) {
|
|
/* assume normalized number */
|
|
cc = 1;
|
|
}
|
|
/* FIXME: denormalized? */
|
|
return cc;
|
|
}
|
|
|
|
/* test data class 64-bit */
|
|
uint32_t HELPER(tcdb)(CPUS390XState *env, uint64_t v1, uint64_t m2)
|
|
{
|
|
int neg = float64_is_neg(v1);
|
|
uint32_t cc = 0;
|
|
|
|
if ((float64_is_zero(v1) && (m2 & (1 << (11-neg)))) ||
|
|
(float64_is_infinity(v1) && (m2 & (1 << (5-neg)))) ||
|
|
(float64_is_any_nan(v1) && (m2 & (1 << (3-neg)))) ||
|
|
(float64_is_signaling_nan(v1, &env->fpu_status) &&
|
|
(m2 & (1 << (1-neg))))) {
|
|
cc = 1;
|
|
} else if (m2 & (1 << (9-neg))) {
|
|
/* assume normalized number */
|
|
cc = 1;
|
|
}
|
|
/* FIXME: denormalized? */
|
|
return cc;
|
|
}
|
|
|
|
/* test data class 128-bit */
|
|
uint32_t HELPER(tcxb)(CPUS390XState *env, uint64_t ah,
|
|
uint64_t al, uint64_t m2)
|
|
{
|
|
float128 v1 = make_float128(ah, al);
|
|
int neg = float128_is_neg(v1);
|
|
uint32_t cc = 0;
|
|
|
|
if ((float128_is_zero(v1) && (m2 & (1 << (11-neg)))) ||
|
|
(float128_is_infinity(v1) && (m2 & (1 << (5-neg)))) ||
|
|
(float128_is_any_nan(v1) && (m2 & (1 << (3-neg)))) ||
|
|
(float128_is_signaling_nan(v1, &env->fpu_status) &&
|
|
(m2 & (1 << (1-neg))))) {
|
|
cc = 1;
|
|
} else if (m2 & (1 << (9-neg))) {
|
|
/* assume normalized number */
|
|
cc = 1;
|
|
}
|
|
/* FIXME: denormalized? */
|
|
return cc;
|
|
}
|
|
|
|
/* square root 32-bit */
|
|
uint64_t HELPER(sqeb)(CPUS390XState *env, uint64_t f2)
|
|
{
|
|
float32 ret = float32_sqrt(f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* square root 64-bit */
|
|
uint64_t HELPER(sqdb)(CPUS390XState *env, uint64_t f2)
|
|
{
|
|
float64 ret = float64_sqrt(f2, &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return ret;
|
|
}
|
|
|
|
/* square root 128-bit */
|
|
uint64_t HELPER(sqxb)(CPUS390XState *env, uint64_t ah, uint64_t al)
|
|
{
|
|
float128 ret = float128_sqrt(make_float128(ah, al), &env->fpu_status);
|
|
handle_exceptions(env, GETPC());
|
|
return RET128(ret);
|
|
}
|
|
|
|
static const int fpc_to_rnd[4] = {
|
|
float_round_nearest_even,
|
|
float_round_to_zero,
|
|
float_round_up,
|
|
float_round_down
|
|
};
|
|
|
|
/* set fpc */
|
|
void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
|
|
{
|
|
/* Install everything in the main FPC. */
|
|
env->fpc = fpc;
|
|
|
|
/* Install the rounding mode in the shadow fpu_status. */
|
|
set_float_rounding_mode(fpc_to_rnd[fpc & 3], &env->fpu_status);
|
|
}
|
|
|
|
/* set fpc and signal */
|
|
void HELPER(sfas)(CPUS390XState *env, uint64_t val)
|
|
{
|
|
uint32_t signalling = env->fpc;
|
|
uint32_t source = val;
|
|
uint32_t s390_exc;
|
|
|
|
/* The contents of the source operand are placed in the FPC register;
|
|
then the flags in the FPC register are set to the logical OR of the
|
|
signalling flags and the source flags. */
|
|
env->fpc = source | (signalling & 0x00ff0000);
|
|
set_float_rounding_mode(fpc_to_rnd[source & 3], &env->fpu_status);
|
|
|
|
/* If any signalling flag is 1 and the corresponding source mask
|
|
is also 1, a simulated-iee-exception trap occurs. */
|
|
s390_exc = (signalling >> 16) & (source >> 24);
|
|
if (s390_exc) {
|
|
ieee_exception(env, s390_exc | 3, GETPC());
|
|
}
|
|
}
|