Skip to content

Commit 6966e0b

Browse files
hdellerMichael Tokarev
authored andcommitted
target/hppa: Fix FPE exceptions
Implement FP exception register #1 (lower 32-bits of 64-bit fr[0]). A proper implementation is necessary to allow the Linux kernel in system mode and the qemu linux-user to send proper si_code values on SIGFPE signal. Always set the T-bit on taken exception, and merge over- and underflow in system mode to just set overflow bit to mimic the behaviour I tested on a physical machine. The test program below can be used to verify correct behaviour. Note that behaviour on SIGFPE may vary on different platforms. The program should always detect the correct signal, but it may or may not be able to sucessfully continue afterwards. #define _GNU_SOURCE #include <signal.h> #include <stdio.h> #include <fenv.h> #include <float.h> static void fpe_func(int sig, siginfo_t *i, void *v) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGFPE); sigprocmask(SIG_UNBLOCK, &set, NULL); printf("GOT signal %d with si_code %ld\n", sig, i->si_code); } int main(int argc, char *argv[]) { struct sigaction action = { .sa_sigaction = fpe_func, .sa_flags = SA_RESTART|SA_SIGINFO }; sigaction(SIGFPE, &action, 0); feenableexcept(FE_OVERFLOW | FE_UNDERFLOW); double x = DBL_MIN; return printf("%lf\n", argc > 1 ? 1.7976931348623158E308*1.7976931348623158E308 : x / 10); } Signed-off-by: Helge Deller <[email protected]> (cherry picked from commit ebd3949) Signed-off-by: Michael Tokarev <[email protected]>
1 parent 5f119c6 commit 6966e0b

File tree

1 file changed

+17
-3
lines changed

1 file changed

+17
-3
lines changed

target/hppa/fpu_helper.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra)
9595
{
9696
uint32_t soft_exp = get_float_exception_flags(&env->fp_status);
9797
uint32_t hard_exp = 0;
98-
uint32_t shadow = env->fr0_shadow;
98+
uint32_t shadow = env->fr0_shadow & 0x3ffffff;
99+
uint32_t fr1 = 0;
99100

100101
if (likely(soft_exp == 0)) {
101102
env->fr[0] = (uint64_t)shadow << 32;
@@ -108,9 +109,22 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra)
108109
hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, R_FPSR_ENA_O_MASK);
109110
hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK);
110111
hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, R_FPSR_ENA_V_MASK);
111-
shadow |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT);
112+
if (hard_exp & shadow) {
113+
shadow = FIELD_DP32(shadow, FPSR, T, 1);
114+
/* fill exception register #1, which is lower 32-bits of fr[0] */
115+
#if !defined(CONFIG_USER_ONLY)
116+
if (hard_exp & (R_FPSR_ENA_O_MASK | R_FPSR_ENA_U_MASK)) {
117+
/* over- and underflow both set overflow flag only */
118+
fr1 = FIELD_DP32(fr1, FPSR, C, 1);
119+
fr1 = FIELD_DP32(fr1, FPSR, FLG_O, 1);
120+
} else
121+
#endif
122+
{
123+
fr1 |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT);
124+
}
125+
}
112126
env->fr0_shadow = shadow;
113-
env->fr[0] = (uint64_t)shadow << 32;
127+
env->fr[0] = (uint64_t)shadow << 32 | fr1;
114128

115129
if (hard_exp & shadow) {
116130
hppa_dynamic_excp(env, EXCP_ASSIST, ra);

0 commit comments

Comments
 (0)