[RFC] x86_64: Simplify powl computation for small integral y [BZ #33411]

Siddhesh Poyarekar siddhesh@sourceware.org
Sat Oct 11 00:44:00 GMT 2025


The powl implementation for x86_64 ends up multiplying X once more than
necessary and then throwing away that result.  This results in an
overflow flag being set in cases where there is no overflow.

Simplify the relevant portion by special casing the -3 to 3 range and
simply multiplying repetitively.

This does not introduce any regressions other than the following:

testing long double (without inline functions)
Failure: Test: pown_upward (-0x1p+8192, 3LL)
Result:
 is:         -inf  -inf
 should be:  -1.18973149535723176502e+4932  -0xf.fffffffffffffff0p+16380
Failure: Test: pown_upward (-0x1p-8192, -3LL)
Result:
 is:         -inf  -inf
 should be:  -1.18973149535723176502e+4932  -0xf.fffffffffffffff0p+16380
Failure: Test: pown_upward (-0x2p-16384, -3LL)
Result:
 is:         -inf  -inf
 should be:  -1.18973149535723176502e+4932  -0xf.fffffffffffffff0p+16380
Failure: Test: pown_upward (-0x4p-16384, -3LL)
Result:
 is:         -inf  -inf
 should be:  -1.18973149535723176502e+4932  -0xf.fffffffffffffff0p+16380
Failure: Test: pown_upward (-0xf.fffffffffffffffp+16380, 3LL)
Result:
 is:         -inf  -inf
 should be:  -1.18973149535723176502e+4932  -0xf.fffffffffffffff0p+16380

The results seem OK to me and I'm inclined to update the results file,
but I'm not entirely sure, so I need someone to tell me I'm right, or
of course, wrong :)

Resolves: BZ #33411
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---

PS: I just noticed that powl does not actually have any test inputs and it's
likely only being tested via powr/pown (I assume? since they get affected by
changes to __ieee754_powl?).  That's why this doesn't have a test yet, I'll
post a proper patch with a test if this approach looks fine.

 sysdeps/x86_64/fpu/e_powl.S | 49 +++++++++++++++----------------------
 1 file changed, 20 insertions(+), 29 deletions(-)

diff --git a/sysdeps/x86_64/fpu/e_powl.S b/sysdeps/x86_64/fpu/e_powl.S
index 620ef765a7..35c50f37f3 100644
--- a/sysdeps/x86_64/fpu/e_powl.S
+++ b/sysdeps/x86_64/fpu/e_powl.S
@@ -144,39 +144,30 @@ ENTRY(__ieee754_powl)
 	fcomip	%st(1), %st	// 4 : y : x
 	fstp	%st(0)		// y : x
 	jnc	3f
-	mov	-8(%rsp),%eax
-	mov	-4(%rsp),%edx
-	orl	$0, %edx
+
+	/* Here onwards, it's just integral y in range [-3, 3].  */
+	movq	-8(%rsp),%rax
+	orq	$0, %rax
 	fstp	%st(0)		// x
 	jns	4f		// y >= 0, jump
 	fdivrl	MO(one)		// 1/x		(now referred to as x)
-	negl	%eax
-	adcl	$0, %edx
-	negl	%edx
+	negq	%rax
 4:	fldl	MO(one)		// 1 : x
-	fxch
-
-	/* If y is even, take the absolute value of x.  Otherwise,
-	   ensure all intermediate values that might overflow have the
-	   sign of x.  */
-	testb	$1, %al
-	jnz	6f
-	fabs
-
-6:	shrdl	$1, %edx, %eax
-	jnc	5f
-	fxch
-	fabs
-	fmul	%st(1)		// x : ST*x
-	fxch
-5:	fld	%st		// x : x : ST*x
-	fabs			// |x| : x : ST*x
-	fmulp			// |x|*x : ST*x
-	shrl	$1, %edx
-	movl	%eax, %ecx
-	orl	%edx, %ecx
-	jnz	6b
-	fstp	%st(0)		// ST*x
+
+	/* y range is further reduced to [0, 3], so just run through the
+	   combination.  */
+	test	%eax, %eax
+	jz	6f
+
+5:	fmul	%st(1)		// RES*x : x
+	subl	$1, %eax
+	test	%eax, %eax
+	jz	6f
+	jmp	5b
+
+	/* Result is in %st(1), so pop the stack once.  */
+6:	fxch
+	fstp	%st(0)		// RES
 	LDBL_CHECK_FORCE_UFLOW_NONNAN
 	ret
 
-- 
2.51.0



More information about the Libc-alpha mailing list