From: Matt Turner <mattst88@gmail.com> Date: 2026-08-03 23:40:48
Three fixes to floating-point exception handling on Alpha. The first two
are a pair and should be applied and backported together; each commit
message explains why taking one alone is worse than taking neither.
The first fixes ieee_swcr_to_fpcr() setting FPCR_DNOD unconditionally,
which disabled denormal operand traps for every process using the default
FPU settings. Instructions built with the software completion suffix
never reached the kernel emulator, and the hardware silently substituted
zero for denormal operands. This affects anything built with -mieee,
glibc included.
The second stops hardware-fabricated exception bits from reaching user
space. On EV6 and later the hardware writes exception status into the
FPCR before delivering a software completion trap, and those bits can be
wrong for the instruction that trapped. alpha_fp_emul() only wrote the
FPCR back when soft-fp raised something, so whenever it found the
instruction exact the fabricated bits stayed visible to fetestexcept().
The exception summary register is now passed down so the handler can tell
which exceptions the hardware attributed to the trapping instruction.
The third makes the emulation determine tininess after rounding, as the
hardware does. soft-fp had no notion of the distinction and always
determined it before rounding, so an operation that trapped for software
completion could report an underflow the same operation would not report
when it did not trap. The two paths disagreed on the same machine, which
IEEE 754 does not allow. This adds _FP_TININESS_AFTER_ROUNDING to the
shared soft-fp code, as glibc's copy already has, and sets it for Alpha
only. It defaults to zero, so powerpc, sh and sparc are unchanged.
Measured with the glibc testsuite on an UP1500 (EV67), with
CONFIG_MATHEMU=y. Without that option alpha_fp_emul() is not built at
all and roughly 830 math tests fail on their own, so it is a prerequisite
for any of this being reachable.
glibc make check subdirs=math
before 831 failures
after patch 1 48 failures
after patch 2 28 failures
after patch 3 (and glibc fixes) 3 failures
The three that remain are a GCC bug: a long double to float cast on Alpha
is lowered as quad to double to float, but C requires it to round once,
and the intermediate can land on a float halfway point. Two glibc fixes
are being posted separately to libc-alpha.
stdlib/tst-tininess passes, which confirms the hardware determines
tininess after rounding for the results it produces itself.
The shared soft-fp change was compile tested on sparc32, sparc64 and
ppc32 in addition to alpha. Only Alpha was tested at runtime; elsewhere
the new code is unreachable by default.
---
Matt Turner (3):
alpha: fix ieee_swcr_to_fpcr setting FPCR_DNOD unconditionally
alpha: don't leak hardware-fabricated FP exception bits to user space
alpha: determine tininess after rounding in the FP emulation
arch/alpha/include/asm/sfp-machine.h | 4 ++
arch/alpha/include/uapi/asm/fpu.h | 8 +++-
arch/alpha/kernel/traps.c | 6 +--
arch/alpha/math-emu/math.c | 88 +++++++++++++++++++++++++++++++-----
include/math-emu/op-common.h | 23 +++++++++-
include/math-emu/soft-fp.h | 8 ++++
6 files changed, 119 insertions(+), 18 deletions(-)
---
base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda
change-id: 20260803-alpha-fp-exceptions-5c77d9e057a3
Best regards,
--
Matt Turner [off-list ref]
From: Matt Turner <mattst88@gmail.com> Date: 2026-08-03 23:40:50
ieee_swcr_to_fpcr() converts the software IEEE trap-enable and status
bits kept in thread_info.ieee_state into the hardware FPCR format. It
contained:
fp |= (~sw & IEEE_TRAP_ENABLE_DNO) << 41;
FPCR_DNOD (bit 47) disables denormal operand traps: with it set the
hardware handles a denormal operand itself, treating it as zero, instead
of trapping for software completion. The intent was to set DNOD when the
user has not asked for SIGFPE on denormal operands, but
IEEE_TRAP_ENABLE_DNO is clear by default, so ieee_swcr_to_fpcr(0) always
set DNOD.
Instructions built with the software completion suffix therefore never
trapped on a denormal operand. The hardware silently substituted zero
and produced wrong results, affecting every program compiled with -mieee
and default FPU settings, glibc included.
Set FPCR_DNOD only when IEEE_MAP_DMZ is requested, which is exactly the
case where flushing denormal inputs to zero is what the user asked for.
DNOD then encodes MAP_DMZ, which ieee_fpcr_to_swcr() already recovers
from FPCR_DNZ, so drop its attempt to recover IEEE_TRAP_ENABLE_DNO from
DNOD; the DNO trap enable lives solely in ieee_state.
Both functions are in a uapi header, so the encoding change is visible to
userspace, but nothing outside the kernel is known to depend on DNOD
carrying the DNO trap enable, and the kernel is the only writer of the
FPCR.
This must not be backported on its own. Re-enabling denormal operand
traps exposes a second bug, fixed in the following patch: those traps
usually find an exact result, and for an exact result the emulator did
not write the FPCR back, leaving hardware-fabricated exception bits
visible to user space. Taken alone this change would make spurious
exception flags more common.
The bug predates the git history, so there is no commit to reference in a
Fixes tag.
Cc: stable@vger.kernel.org # 5.15+
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
arch/alpha/include/uapi/asm/fpu.h | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
From: Matt Turner <mattst88@gmail.com> Date: 2026-08-03 23:40:52
On EV6 and later the hardware records exception status bits in the FPCR
before delivering a software completion trap, and those bits can be wrong
for the instruction that trapped. Converting a double that is exactly
representable as a subnormal float sets FPCR_UNF even though the result
is exact, and an underflow trap additionally sets FPCR_INE even when the
emulated operation turns out to be exact.
alpha_fp_emul() only wrote the FPCR when soft-fp raised an exception, so
whenever it determined that the instruction was exact the fabricated bits
stayed in the FPCR and were reported to user space by fetestexcept().
Pass the exception summary register down from do_entArith() so the
handler can tell which exceptions the hardware attributed to the trapping
instruction, and always write the FPCR. Clear the exceptions that the
trap reported but that soft-fp did not raise. EXC_SUM reports only the
underflow or overflow when the hardware also set INE, so treat INE as a
candidate in that case, and treat a trap with no reported exception as a
denormal operand trap, for which the hardware can fabricate INE and UNF
as well. Bits that software has already confirmed in ieee_state belong
to this or an earlier instruction and are never cleared.
The imprecise path passes no summary. There the trap was taken somewhere
in the trap shadow, so EXC_SUM is not attribution for the instruction
being re-executed -- and only EV6, which traps precisely and so never
takes that path, has fabricated bits to clear. For the same reason the
clearing is guarded by implver(), matching swcr_update_status().
On an UP1500 (EV68) this takes the glibc math testsuite from 831 failures
to 28, the remainder being unrelated to exception status.
This belongs with the preceding fix to ieee_swcr_to_fpcr(), and should
not be backported without it -- nor it without this. That fix stops
FPCR_DNOD being set unconditionally, so denormal operand traps start
firing again. Those traps very often find an exact result, which is
precisely the case where the old code left the FPCR unwritten and the
fabricated bits visible. Applied alone it would make spurious exception
flags more common, not less.
One case cannot be resolved here: an inexact instruction without the
software completion suffix never traps, so its INE reaches the FPCR
without being recorded anywhere else. Such a bit is indistinguishable
from an INE the hardware fabricated for a trapping instruction, and is
lost if an underflow or overflow trap with an exact result follows it.
The FPCR is the only record of those instructions and it carries no
attribution.
The bug predates the git history, so there is no commit to reference in a
Fixes tag.
Cc: stable@vger.kernel.org # 5.15+
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
arch/alpha/kernel/traps.c | 6 ++--
arch/alpha/math-emu/math.c | 88 ++++++++++++++++++++++++++++++++++++++++------
2 files changed, 80 insertions(+), 14 deletions(-)
@@ -185,7 +185,7 @@ do_entArith(unsigned long summary, unsigned long write_mask,emulatetheinstruction.Iftheprocessorsupportspreciseexceptions,wedon'thavetosearch.*/if(!amask(AMASK_PRECISE_TRAP))-si_code=alpha_fp_emul(regs->pc-4);+si_code=alpha_fp_emul(regs->pc-4,summary);elsesi_code=alpha_fp_emul_imprecise(regs,write_mask);if(si_code==0)
@@ -300,12 +315,56 @@ alpha_fp_emul (unsigned long pc)swcr|=(_fex<<IEEE_STATUS_TO_EXCSUM_SHIFT);current_thread_info()->ieee_state|=(_fex<<IEEE_STATUS_TO_EXCSUM_SHIFT);+}-/* Update hardware control register. */-fpcr&=(~FPCR_MASK|FPCR_DYN_MASK);-fpcr|=ieee_swcr_to_fpcr(swcr);-wrfpcr(fpcr);+/*+*EV6recordsexceptionstatusbitsintheFPCRbeforedeliveringthe+*softwarecompletiontrap,andswcr_update_status()abovemergedthem+*intoSWCR.Somecanbewrongfortheinstructionwejustemulated:+*aCVTTSofavalueexactlyrepresentableasasubnormalsetsFPCR_UNF+*eventhoughtheresultisexact.Cleartheexceptionsthetrap+*reportedbutthatsoft-fpdidnotraise.+*/+if(implver()==IMPLVER_EV6){+unsignedlongspurious=summary&EXC_SUM_MASK;+if(spurious&(EXC_SUM_UNF|EXC_SUM_OVF)){+/*+*EXC_SUMreportsonlytheunderfloworoverflow,+*butthehardwaresetsINEalongsideitintheFPCR.+*/+spurious|=EXC_SUM_INE;+}elseif(!spurious){+/*+*Noexceptionreported,sothiswasadenormal+*operandtrap,forwhichINEandUNFcanbe+*fabricatedaswell.+*/+spurious=EXC_SUM_INE|EXC_SUM_UNF;+}++/*+*Neverclearanexceptionsoftwarehasconfirmed.Every+*instructionthatgenuinelyraisesonetrapsforsoftware+*completionandisrecordedinieee_stateabove,soabit+*foundthere--includingonejustsetfrom_fex--belongs+*tothisoranearlierinstructionandmustsurvive.+*/+spurious&=~(current_thread_info()->ieee_state+>>IEEE_STATUS_TO_EXCSUM_SHIFT);++swcr&=~(spurious<<IEEE_STATUS_TO_EXCSUM_SHIFT);+}++/*+*Updatehardwarecontrolregister.Thishastohappenevenwhen+*soft-fpraisednothing,toclearanyfabricatedbits.+*/+fpcr&=(~FPCR_MASK|FPCR_DYN_MASK);+fpcr|=ieee_swcr_to_fpcr(swcr);+wrfpcr(fpcr);++if(_fex){/* Do we generate a signal? */_fex=_fex&swcr&IEEE_TRAP_ENABLE_MASK;si_code=0;
@@ -387,9 +446,16 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)break;}if(!write_mask){-/* Re-execute insns in the trap-shadow. */+/*+*Re-executeinsnsinthetrap-shadow.Passno+*exceptionsummary:itdescribesthetrap,which+*wastakenanywhereintheshadow,andsoisnot+*attributionforthisinstruction.Nothingis+*lost,sinceonlyEV6--whichtrapspreciselyand+*nevercomesthisway--needsit.+*/regs->pc=trigger_pc+4;-si_code=alpha_fp_emul(trigger_pc);+si_code=alpha_fp_emul(trigger_pc,0);gotoegress;}trigger_pc-=4;
From: Matt Turner <mattst88@gmail.com> Date: 2026-08-03 23:40:53
IEEE 754 lets an architecture determine tininess of a floating-point
result either before or after rounding, but requires the same choice for
every operation. Alpha determines it after rounding, and stdlib's
tst-tininess confirms the hardware does so for the results it produces
itself.
The soft-fp emulation has no notion of the distinction and always
determines tininess before rounding, so a result that the hardware would
not consider tiny is reported as underflowing whenever the instruction
happens to trap for software completion. The two paths then disagree on
the same machine. A multiply of the largest subnormal double by
1 + 2^-52 rounds up to the smallest normal and raises no underflow when
the operands are normal, but raises it when an operand is subnormal and
the instruction traps:
glibc math testsuite, test-float32x-float64-mul:
Failure: mul_double (0x3.ffffffffffffcp-1024, 0x1.0000000000001p+0):
Exception "Underflow" set
Add _FP_TININESS_AFTER_ROUNDING, as glibc's copy of soft-fp has, and set
it for alpha. It determines tininess by rounding a copy of the result as
if the exponent range were unbounded, which is what the definition asks
for; a plain check of whether the rounded result came out normal is not
equivalent and would be wrong for values that stay tiny under an
unbounded exponent range but round up to the smallest normal in the
subnormal grid. The macro defaults to zero, so powerpc, sh and sparc
keep determining tininess before rounding as they do now.
Cc: stable@vger.kernel.org # 5.15+
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
arch/alpha/include/asm/sfp-machine.h | 4 ++++
include/math-emu/op-common.h | 23 +++++++++++++++++++++--
include/math-emu/soft-fp.h | 8 ++++++++
3 files changed, 33 insertions(+), 2 deletions(-)
@@ -59,6 +59,10 @@R##_c=FP_CLS_NAN;\}while(0)+/* Alpha determines tininess after rounding, so the emulation must do the+sameasthehardwaredoesfortheresultsitproducesitself.*/+#define _FP_TININESS_AFTER_ROUNDING 1+/* Obtain the current rounding mode. */#define FP_ROUNDMODE mode#define FP_RND_NEAREST (FPCR_DYN_NORMAL >> FPCR_DYN_SHIFT)
@@ -135,6 +135,24 @@ do { \else\{\/* we've got a denormalized number */\+int_FP_PACK_CANONICAL_is_tiny=1;\+if(_FP_TININESS_AFTER_ROUNDING&&X##_e==0)\+{\+/* Architectures that detect tininess after rounding \+onlysignalunderflowiftheresultisstill\+subnormalonceroundedasiftheexponentrange\+wereunbounded.Roundacopytofindout.*/\+FP_DECL_##fs(_FP_PACK_CANONICAL_T);\+/* The class field is not used by the rounding below, \+andisunusedentirelywherethisblockisdead.*/\+(void)_FP_PACK_CANONICAL_T##_c;\+_FP_FRAC_COPY_##wc(_FP_PACK_CANONICAL_T,X);\+_FP_PACK_CANONICAL_T##_s=X##_s;\+_FP_PACK_CANONICAL_T##_e=X##_e;\+_FP_ROUND(wc,_FP_PACK_CANONICAL_T);\+if(_FP_FRAC_OVERP_##wc(fs,_FP_PACK_CANONICAL_T))\+_FP_PACK_CANONICAL_is_tiny=0;\+}\X##_e=-X##_e+1;\if(X##_e<=_FP_WFRACBITS_##fs)\{\
@@ -161,8 +179,9 @@ do { \_FP_FRAC_SRL_##wc(X,_FP_WORKBITS);\}\}\-if((FP_CUR_EXCEPTIONS&FP_EX_INEXACT)||\-(FP_TRAPPING_EXCEPTIONS&FP_EX_UNDERFLOW))\+if(_FP_PACK_CANONICAL_is_tiny\+&&((FP_CUR_EXCEPTIONS&FP_EX_INEXACT)||\+(FP_TRAPPING_EXCEPTIONS&FP_EX_UNDERFLOW)))\FP_SET_EXCEPTION(FP_EX_UNDERFLOW);\}\else\
From: Magnus Lindholm <linmag7@gmail.com> Date: 2026-08-05 09:25:02
On Tue, Aug 4, 2026 at 1:40 AM Matt Turner [off-list ref] wrote:
quoted hunk
IEEE 754 lets an architecture determine tininess of a floating-point
result either before or after rounding, but requires the same choice for
every operation. Alpha determines it after rounding, and stdlib's
tst-tininess confirms the hardware does so for the results it produces
itself.
The soft-fp emulation has no notion of the distinction and always
determines tininess before rounding, so a result that the hardware would
not consider tiny is reported as underflowing whenever the instruction
happens to trap for software completion. The two paths then disagree on
the same machine. A multiply of the largest subnormal double by
1 + 2^-52 rounds up to the smallest normal and raises no underflow when
the operands are normal, but raises it when an operand is subnormal and
the instruction traps:
glibc math testsuite, test-float32x-float64-mul:
Failure: mul_double (0x3.ffffffffffffcp-1024, 0x1.0000000000001p+0):
Exception "Underflow" set
Add _FP_TININESS_AFTER_ROUNDING, as glibc's copy of soft-fp has, and set
it for alpha. It determines tininess by rounding a copy of the result as
if the exponent range were unbounded, which is what the definition asks
for; a plain check of whether the rounded result came out normal is not
equivalent and would be wrong for values that stay tiny under an
unbounded exponent range but round up to the smallest normal in the
subnormal grid. The macro defaults to zero, so powerpc, sh and sparc
keep determining tininess before rounding as they do now.
Cc: stable@vger.kernel.org # 5.15+
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
arch/alpha/include/asm/sfp-machine.h | 4 ++++
include/math-emu/op-common.h | 23 +++++++++++++++++++++--
include/math-emu/soft-fp.h | 8 ++++++++
3 files changed, 33 insertions(+), 2 deletions(-)
@@ -59,6 +59,10 @@R##_c=FP_CLS_NAN;\}while(0)+/* Alpha determines tininess after rounding, so the emulation must do the+sameasthehardwaredoesfortheresultsitproducesitself.*/+#define _FP_TININESS_AFTER_ROUNDING 1+/* Obtain the current rounding mode. */#define FP_ROUNDMODE mode#define FP_RND_NEAREST (FPCR_DYN_NORMAL >> FPCR_DYN_SHIFT)
@@ -135,6 +135,24 @@ do { \else\{\/* we've got a denormalized number */\+int_FP_PACK_CANONICAL_is_tiny=1;\+if(_FP_TININESS_AFTER_ROUNDING&&X##_e==0)\+{\+/* Architectures that detect tininess after rounding \+onlysignalunderflowiftheresultisstill\+subnormalonceroundedasiftheexponentrange\+wereunbounded.Roundacopytofindout.*/\+FP_DECL_##fs(_FP_PACK_CANONICAL_T);\+/* The class field is not used by the rounding below, \+andisunusedentirelywherethisblockisdead.*/\+(void)_FP_PACK_CANONICAL_T##_c;\+_FP_FRAC_COPY_##wc(_FP_PACK_CANONICAL_T,X);\+_FP_PACK_CANONICAL_T##_s=X##_s;\+_FP_PACK_CANONICAL_T##_e=X##_e;\+_FP_ROUND(wc,_FP_PACK_CANONICAL_T);\+if(_FP_FRAC_OVERP_##wc(fs,_FP_PACK_CANONICAL_T))\+_FP_PACK_CANONICAL_is_tiny=0;\+}\X##_e=-X##_e+1;\if(X##_e<=_FP_WFRACBITS_##fs)\{\
@@ -161,8 +179,9 @@ do { \_FP_FRAC_SRL_##wc(X,_FP_WORKBITS);\}\}\-if((FP_CUR_EXCEPTIONS&FP_EX_INEXACT)||\-(FP_TRAPPING_EXCEPTIONS&FP_EX_UNDERFLOW))\+if(_FP_PACK_CANONICAL_is_tiny\+&&((FP_CUR_EXCEPTIONS&FP_EX_INEXACT)||\+(FP_TRAPPING_EXCEPTIONS&FP_EX_UNDERFLOW)))\FP_SET_EXCEPTION(FP_EX_UNDERFLOW);\}\else\
@@ -31,6 +31,14 @@#include<endian.h>#endif+/* Whether the architecture determines tininess of a floating-point+resultafterroundingratherthanbeforeit.IEEE754permitseither+butrequiresthesamechoiceforeveryoperation,sothishastoagree+withwhatthehardwaredoesfortheresultsitproducesitself.*/+#ifndef _FP_TININESS_AFTER_ROUNDING+#define _FP_TININESS_AFTER_ROUNDING 0+#endif+#define _FP_WORKBITS 3#define _FP_WORK_LSB ((_FP_W_TYPE)1 << 3)#define _FP_WORK_ROUND ((_FP_W_TYPE)1 << 2)--
2.54.0
Hi,
The generic soft-fp policy switch together with the Alpha-specific
selection looks like the right approach. The default preserves the
existing behaviour for the other soft-fp users, so I do not see an
expected behavioural change outside Alpha.
I tested the complete series with CONFIG_MATHEMU=y on the same machine
and using the same glibc build. The kernel boots normally, and the
glibc math testsuite improves from 48 to 28 failing test programs, with
no new failing tests. In particular, math/test-float32x-float64-mul,
which exercises the incorrect tininess handling described in the commit
message, now passes.
Reviewed-by: Magnus Lindholm linmag7@gmail.com
Tested-by: Magnus Lindholm linmag7@gmail.com
From: Magnus Lindholm <linmag7@gmail.com> Date: 2026-08-05 09:29:09
On Tue, Aug 4, 2026 at 1:40 AM Matt Turner [off-list ref] wrote:
quoted hunk
ieee_swcr_to_fpcr() converts the software IEEE trap-enable and status
bits kept in thread_info.ieee_state into the hardware FPCR format. It
contained:
fp |= (~sw & IEEE_TRAP_ENABLE_DNO) << 41;
FPCR_DNOD (bit 47) disables denormal operand traps: with it set the
hardware handles a denormal operand itself, treating it as zero, instead
of trapping for software completion. The intent was to set DNOD when the
user has not asked for SIGFPE on denormal operands, but
IEEE_TRAP_ENABLE_DNO is clear by default, so ieee_swcr_to_fpcr(0) always
set DNOD.
Instructions built with the software completion suffix therefore never
trapped on a denormal operand. The hardware silently substituted zero
and produced wrong results, affecting every program compiled with -mieee
and default FPU settings, glibc included.
Set FPCR_DNOD only when IEEE_MAP_DMZ is requested, which is exactly the
case where flushing denormal inputs to zero is what the user asked for.
DNOD then encodes MAP_DMZ, which ieee_fpcr_to_swcr() already recovers
from FPCR_DNZ, so drop its attempt to recover IEEE_TRAP_ENABLE_DNO from
DNOD; the DNO trap enable lives solely in ieee_state.
Both functions are in a uapi header, so the encoding change is visible to
userspace, but nothing outside the kernel is known to depend on DNOD
carrying the DNO trap enable, and the kernel is the only writer of the
FPCR.
This must not be backported on its own. Re-enabling denormal operand
traps exposes a second bug, fixed in the following patch: those traps
usually find an exact result, and for an exact result the emulator did
not write the FPCR back, leaving hardware-fabricated exception bits
visible to user space. Taken alone this change would make spurious
exception flags more common.
The bug predates the git history, so there is no commit to reference in a
Fixes tag.
Cc: stable@vger.kernel.org # 5.15+
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
arch/alpha/include/uapi/asm/fpu.h | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
@@ -101,7 +101,12 @@ ieee_swcr_to_fpcr(unsigned long sw)|IEEE_TRAP_ENABLE_OVF))<<48;fp|=(~sw&(IEEE_TRAP_ENABLE_UNF|IEEE_TRAP_ENABLE_INE))<<57;fp|=(sw&IEEE_MAP_UMZ?FPCR_UNDZ|FPCR_UNFD:0);-fp|=(~sw&IEEE_TRAP_ENABLE_DNO)<<41;+/*+*Disabledenormaloperandtrapsonlywhendenormalinputsaretobe+*flushedtozero.Otherwisetheymustkeeptrapping,sothat/S+*instructionsreachthekernelemulationhandler.+*/+fp|=(sw&IEEE_MAP_DMZ?FPCR_DNOD:0);returnfp;}
@@ -116,7 +121,6 @@ ieee_fpcr_to_swcr(unsigned long fp)|IEEE_TRAP_ENABLE_OVF);sw|=(~fp>>57)&(IEEE_TRAP_ENABLE_UNF|IEEE_TRAP_ENABLE_INE);sw|=(fp>>47)&IEEE_MAP_UMZ;-sw|=(~fp>>41)&IEEE_TRAP_ENABLE_DNO;returnsw;}--
2.54.0
Hi,
The new mapping of FPCR_DNOD to IEEE_MAP_DMZ looks correct to me.
I tested the complete series with CONFIG_MATHEMU=y on the same machine
and using the same glibc build. The kernel boots normally, and the
glibc math testsuite improves from 48 to 28 failing test programs, with
no new failing tests.
Reviewed-by: Magnus Lindholm linmag7@gmail.com
Tested-by: Magnus Lindholm linmag7@gmail.com
From: Magnus Lindholm <linmag7@gmail.com> Date: 2026-08-05 09:34:09
On Tue, Aug 4, 2026 at 1:40 AM Matt Turner [off-list ref] wrote:
On EV6 and later the hardware records exception status bits in the FPCR
before delivering a software completion trap, and those bits can be wrong
for the instruction that trapped. Converting a double that is exactly
representable as a subnormal float sets FPCR_UNF even though the result
is exact, and an underflow trap additionally sets FPCR_INE even when the
emulated operation turns out to be exact.
alpha_fp_emul() only wrote the FPCR when soft-fp raised an exception, so
whenever it determined that the instruction was exact the fabricated bits
stayed in the FPCR and were reported to user space by fetestexcept().
Pass the exception summary register down from do_entArith() so the
handler can tell which exceptions the hardware attributed to the trapping
instruction, and always write the FPCR. Clear the exceptions that the
trap reported but that soft-fp did not raise. EXC_SUM reports only the
underflow or overflow when the hardware also set INE, so treat INE as a
candidate in that case, and treat a trap with no reported exception as a
denormal operand trap, for which the hardware can fabricate INE and UNF
as well. Bits that software has already confirmed in ieee_state belong
to this or an earlier instruction and are never cleared.
The imprecise path passes no summary. There the trap was taken somewhere
in the trap shadow, so EXC_SUM is not attribution for the instruction
being re-executed -- and only EV6, which traps precisely and so never
takes that path, has fabricated bits to clear. For the same reason the
clearing is guarded by implver(), matching swcr_update_status().
On an UP1500 (EV68) this takes the glibc math testsuite from 831 failures
to 28, the remainder being unrelated to exception status.
This belongs with the preceding fix to ieee_swcr_to_fpcr(), and should
not be backported without it -- nor it without this. That fix stops
FPCR_DNOD being set unconditionally, so denormal operand traps start
firing again. Those traps very often find an exact result, which is
precisely the case where the old code left the FPCR unwritten and the
fabricated bits visible. Applied alone it would make spurious exception
flags more common, not less.
One case cannot be resolved here: an inexact instruction without the
software completion suffix never traps, so its INE reaches the FPCR
without being recorded anywhere else. Such a bit is indistinguishable
from an INE the hardware fabricated for a trapping instruction, and is
lost if an underflow or overflow trap with an exact result follows it.
The FPCR is the only record of those instructions and it carries no
attribution.
The bug predates the git history, so there is no commit to reference in a
Fixes tag.
Cc: stable@vger.kernel.org # 5.15+
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
arch/alpha/kernel/traps.c | 6 ++--
arch/alpha/math-emu/math.c | 88 ++++++++++++++++++++++++++++++++++++++++------
2 files changed, 80 insertions(+), 14 deletions(-)
Hi,
Passing EXC_SUM to the precise emulation path and always writing the
corrected FPCR state back looks correct to me.
I tested the complete series with CONFIG_MATHEMU=y on the same machine
and using the same glibc build. The kernel boots normally, and the
glibc math testsuite improves from 48 to 28 failing test programs, with
no new failing tests. Tested on Alphaserver ES40.
Reviewed-by: Magnus Lindholm linmag7@gmail.com
Tested-by: Magnus Lindholm linmag7@gmail.com
From: Magnus Lindholm <linmag7@gmail.com> Date: 2026-09-04 06:13:57
Hi everyone,
Adding Michael, Yoshinori, Rich and Adrian directly, although this
is an alpha series, patch 3/3 also touches the shared soft-fp code in
include/math-emu/ (op-common.h, soft-fp.h), which your architectures
build against too (confirmed by grepping arch/{powerpc,sh,sparc} for
includes of those headers; parisc/m68k/mips/x86 have their own
separate math-emu and aren't touched).
The new _FP_TININESS_AFTER_ROUNDING macro defaults to 0, so the
intent is no behavioural change on your side. The new guard it adds
in op-common.h is a regular runtime `if` rather than `#if` though, so
it still gets compiled against your macro instantiations even if it
should dead-code-eliminate at -O.
No action needed if it builds fine for you, just flagging since it's
tagged for stable. Let us know if it causes any problems, or feel
free to ack if you're happy with it.
I've reviewed and tested patch 3/3 on alpha already (Reviewed-by/
Tested-by posted).
Thanks,
Magnus
From: Matt Turner <mattst88@gmail.com> Date: 2026-10-08 19:29:42
On Fri, Sep 4, 2026 at 2:13 AM Magnus Lindholm [off-list ref] wrote:
Hi everyone,
Adding Michael, Yoshinori, Rich and Adrian directly, although this
is an alpha series, patch 3/3 also touches the shared soft-fp code in
include/math-emu/ (op-common.h, soft-fp.h), which your architectures
build against too (confirmed by grepping arch/{powerpc,sh,sparc} for
includes of those headers; parisc/m68k/mips/x86 have their own
separate math-emu and aren't touched).
The new _FP_TININESS_AFTER_ROUNDING macro defaults to 0, so the
intent is no behavioural change on your side. The new guard it adds
in op-common.h is a regular runtime `if` rather than `#if` though, so
it still gets compiled against your macro instantiations even if it
should dead-code-eliminate at -O.
No action needed if it builds fine for you, just flagging since it's
tagged for stable. Let us know if it causes any problems, or feel
free to ack if you're happy with it.
I've reviewed and tested patch 3/3 on alpha already (Reviewed-by/
Tested-by posted).
Thanks,
Magnus
Hello all,
Could a maintainer of this code find time to have a look?
We can take it through the alpha tree, but we'd certainly prefer some
review first.
Thanks,
Matt