Problem
FST and FSTP store an x87 floating-point value to memory. If a numeric underflow or
overflow exception is unmasked, the instruction must suppress the store and clear C1, an
x87 condition flag used to report rounding. Unmasked means the corresponding exception
is enabled in the control word.
The source correctly suppresses the store but can leave C1 set. It first sets
C1 from its attempted rounded result, then checks whether the exception prevents the
write:
FP87_Status.C1 := Bit(rounded_up);
...
let write_result := not FP87::Is_Unmasked_Exception(...);
The later exception handling does not clear C1 for the underflow case below.
Reproducing case
Here m32fp is a 32-bit floating-point memory destination. In D9 /2, D9 is the opcode
and 2 is the register-selection field of the following ModR/M byte; the other fields
select the memory address.
Use 64-bit mode with x87 enabled: CR0.EM=0 and CR0.TS=0 allow these instructions to
execute with x87 enabled. ST(0) is the top x87 register and
ST(1) is the next. The control word selects rounding and which exceptions are masked.
“Masked” means the instruction uses the defined fallback behavior for that exception.
Execute FST m32fp (D9 /2) with ST(0) equal to the smallest positive
extended-precision subnormal, a nonzero value below the smallest normal value (raw
80-bit encoding 00000000000000000001), x87 control word 0x0b6f, and a writable
four-byte destination.
With ST(0) nonempty and the initial exception flags clear, source execution records
status & 0x023f = 0x0210 (underflow and C1) and unchanged destination bytes.
The hexadecimal mask keeps only C1 and the six exception flags. It
excludes TOP (the register-stack index), ES (exception summary) and B (busy); these are
not full status words.
Each 20-digit hexadecimal input specifies the exact 80-bit x87 register contents.
Source checked: Intel SDM executable specification revision
d307f89f742765865b87c5d4d23f552b3c72e871.
Separate hardware test
A separate hardware test on an AMD EPYC-Milan processor recorded
status & 0x023f = 0x0010 (underflow, C1 clear) and left the destination unchanged.
This agrees with the manual's C1 requirement.
Manual reference
References use Intel SDM 325462-089US, October 2025.
Intel SDM Volume 1, section 8.5.6, page 8-30 (PDF page 238) states that, when
unmasked numeric overflow or underflow affects a memory store, the inexact
condition is not reported and C1 is cleared. Volume 2A, FST/FSTP, page
3-379 (PDF page 1075) describes the store, its exception flags, and C1.
Proposed fix
Clear C1 for an unmasked numeric overflow or underflow before processing the exceptions,
immediately after the endif following the assignment of FP87_Status.C1 (source line
215):
UE and OE are the underflow and overflow status flags. UM and OM are their control-word
mask bits; zero enables the corresponding exception.
if (exceptions.UE == 0b1 and FP87_Control.UM == 0b0) or
(exceptions.OE == 0b1 and FP87_Control.OM == 0b0) then
FP87_Status.C1 := 0b0;
endif;
Keep the current write-suppression behavior. The same correction applies to binary32 and
binary64 memory stores. The helper also has a separate case where rounding produces
infinity without setting the overflow flag. That flag must be corrected before this C1
check can handle that case. The reproducer here uses underflow and does not depend on
that separate correction.
AI disclosure
Assisted-by: Codex
Codex assisted with source analysis, test review, and drafting this report.
Problem
FST and FSTP store an x87 floating-point value to memory. If a numeric underflow or
overflow exception is unmasked, the instruction must suppress the store and clear C1, an
x87 condition flag used to report rounding. Unmasked means the corresponding exception
is enabled in the control word.
The source correctly suppresses the store but can leave C1 set. It first sets
C1 from its attempted rounded result, then checks whether the exception prevents the
write:
The later exception handling does not clear C1 for the underflow case below.
Reproducing case
Here
m32fpis a 32-bit floating-point memory destination. InD9 /2, D9 is the opcodeand 2 is the register-selection field of the following ModR/M byte; the other fields
select the memory address.
Use 64-bit mode with x87 enabled: CR0.EM=0 and CR0.TS=0 allow these instructions to
execute with x87 enabled. ST(0) is the top x87 register and
ST(1) is the next. The control word selects rounding and which exceptions are masked.
“Masked” means the instruction uses the defined fallback behavior for that exception.
Execute
FST m32fp(D9 /2) with ST(0) equal to the smallest positiveextended-precision subnormal, a nonzero value below the smallest normal value (raw
80-bit encoding
00000000000000000001), x87 control word0x0b6f, and a writablefour-byte destination.
With ST(0) nonempty and the initial exception flags clear, source execution records
status & 0x023f = 0x0210(underflow and C1) and unchanged destination bytes.The hexadecimal mask keeps only C1 and the six exception flags. It
excludes TOP (the register-stack index), ES (exception summary) and B (busy); these are
not full status words.
Each 20-digit hexadecimal input specifies the exact 80-bit x87 register contents.
Source checked: Intel SDM executable specification revision
d307f89f742765865b87c5d4d23f552b3c72e871.Separate hardware test
A separate hardware test on an AMD EPYC-Milan processor recorded
status & 0x023f = 0x0010(underflow, C1 clear) and left the destination unchanged.This agrees with the manual's C1 requirement.
Manual reference
References use Intel SDM 325462-089US, October 2025.
Intel SDM Volume 1, section 8.5.6, page 8-30 (PDF page 238) states that, when
unmasked numeric overflow or underflow affects a memory store, the inexact
condition is not reported and C1 is cleared. Volume 2A, FST/FSTP, page
3-379 (PDF page 1075) describes the store, its exception flags, and C1.
Proposed fix
Clear C1 for an unmasked numeric overflow or underflow before processing the exceptions,
immediately after the
endiffollowing the assignment ofFP87_Status.C1(source line215):
UE and OE are the underflow and overflow status flags. UM and OM are their control-word
mask bits; zero enables the corresponding exception.
Keep the current write-suppression behavior. The same correction applies to binary32 and
binary64 memory stores. The helper also has a separate case where rounding produces
infinity without setting the overflow flag. That flag must be corrected before this C1
check can handle that case. The reproducer here uses underflow and does not depend on
that separate correction.
AI disclosure
Assisted-by: Codex
Codex assisted with source analysis, test review, and drafting this report.