Files
TheRock/compiler/amd-llvm/compiler-rt/lib/builtins/arm/thumb1/mulsf3.S
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

252 lines
11 KiB
ArmAsm

//===-- mulsf3.S - single-precision floating point multiplication ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements single-precision soft-float multiplication with the
// IEEE-754 default rounding (to nearest, ties to even), in optimized Thumb1
// assembly language.
//
//===----------------------------------------------------------------------===//
#include "../../assembly.h"
.syntax unified
.text
.thumb
.p2align 2
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_fmul, __mulsf3)
DEFINE_COMPILERRT_THUMB_FUNCTION(__mulsf3)
push {r4,r5,r6,lr}
// Get exponents of the inputs, and check for uncommon values. In the process
// of this we also compute the sign, because it's marginally quicker that
// way.
lsls r2, r0, #1
adcs r4, r4, r4 // set r4[0] to sign bit of x
lsls r3, r1, #1
adcs r4, r4, r3 // set r4[0] to the output sign
lsrs r2, r2, #24
beq LOCAL_LABEL(zerodenorm0) // still do the next LSRS
lsrs r3, r3, #24
beq LOCAL_LABEL(zerodenorm)
cmp r2, #255
beq LOCAL_LABEL(naninf)
cmp r3, #255
beq LOCAL_LABEL(naninf)
// Compute the output exponent. We'll be generating our product _without_ the
// leading bit, so we subtract 0x7f rather than 0x80.
adds r2, r2, r3
subs r2, r2, #0x7f
// Blank off everything above the mantissas.
lsls r0, r0, #9
lsls r1, r1, #9
LOCAL_LABEL(normalised): // we may come back here from zerodenorm
lsrs r0, r0, #9
lsrs r1, r1, #9
// Multiply. r0 and r1 are the mantissas of the inputs but without their
// leading bits, so the product we want in principle is P=(r0+2^23)(r1+2^23).
// P is at most (2^24-1)^2 < 2^48, so it fits in a word and a half.
//
// The technique below will actually compute P - 2^46, by not adding on the
// term where the two 2^23 are multiplied. The 48-bit result will be
// delivered in two output registers, one containing its bottom 32 bits and
// the other containing the top 32, so they overlap in the middle 16 bits.
// This is done using only two multiply instructions and some bookkeeping.
//
// In the comments I'll write X and Y for the original input mantissas (again
// without their leading bits). I'll also decompose them as X = xh + xl and
// Y = yh + yl, where xl and yl are in the range 0..2^8-1 and xh,yh are
// multiples of 2^8.
adds r5, r0, r1
lsls r5, r5, #7 // r5 = (X+Y) << 7
movs r6, r0
muls r6, r1, r6 // r6 is congruent mod 2^32 to X*Y
lsrs r0, r0, #8
lsrs r1, r1, #8
muls r0, r1, r0
lsls r1, r0, #16 // r1 is congruent mod 2^32 to xh*yh
subs r3, r6, r1 // now r3 is congruent mod 2^32 to
// (X*Y) - (xh*yh) = xh*yl + xl*yh + xl*yl
// and hence, since that is at most 0xfeff0001,
// is _exactly_ equal to that
adds r0, r0, r5 // r0 is now (xh*yh + (X+Y)<<23) >> 16
lsrs r1, r3, #16 // r1 is the top 16 bits of r3, i.e.
// (xh*yl + xl*yh + xl*yl) >> 16
adds r3, r0, r1 // now r3 equals
// (xh*yh + xh*yl + xl*yh + xl*yl + (X+Y)<<23) >> 16
// i.e. (X*Y + (X+Y)<<23) >> 16,
// i.e. (the right answer) >> 16.
// Meanwhile, r6 is exactly the bottom 32 bits of the
// right answer.
// Renormalise if necessary.
lsrs r1, r3, #30
beq LOCAL_LABEL(norenorm)
// Here we have to do something fiddly. Renormalisation would be a trivial
// job if we had the leading mantissa bit - just note that it's one bit
// position above where it should be, and shift right by one. But without
// that bit, we currently have (2x - 2^30), and we want (x - 2^30); just
// shifting right would of course give us (x - 2^29), so we must subtract an
// extra 2^29 to fix this up.
lsrs r3, r3, #1
movs r1, #1
lsls r1, r1, #29
subs r3, r3, r1
adds r2, r2, #1
LOCAL_LABEL(norenorm):
// Round and shift down to the right bit position.
lsrs r0, r3, #7 // round bit goes into the carry flag
bcc LOCAL_LABEL(rounded)
adds r0, r0, #1
// In the round-up branch, we must also check if we have to round to even, by
// testing all the bits below the round bit. We will normally not expect to,
// so we do RTE by branching out of line and back again to avoid spending a
// branch in the common case.
lsls r5, r3, #32-7+1 // check the bits shifted out of r3 above
bne LOCAL_LABEL(rounded) // if any is nonzero, we're not rounding to even
lsls r5, r6, #15 // check the bottom 17 bits of the low-order 32
// (enough to overlap r3 even if we renormalised)
beq LOCAL_LABEL(rte) // if any is nonzero, fall through, else RTE
LOCAL_LABEL(rounded):
// Put on the sign and exponent, check for underflow and overflow, and
// return.
//
// Underflow occurs iff r2 (the output exponent) <= 0. Overflow occurs if
// it's >= 0xFF. (Also if it's 0xFE and we rounded up to overflow, but since
// this code doesn't report exceptions, we can ignore this case because it'll
// happen to return the right answer regardless). So we handle most of this
// via an unsigned comparison against 0xFF, which leaves the one case of a
// zero exponent that we have to filter separately by testing the Z flag
// after we shift the exponent back up into place.
cmp r2, #0xFF // check for most over/underflows
bhs LOCAL_LABEL(outflow) // ... and branch out of line for them
lsls r5, r2, #23 // shift the exponent into its output location
beq LOCAL_LABEL(outflow) // ... and branch again if it was 0
lsls r4, r4, #31 // shift the output sign into place
orrs r0, r0, r4 // and OR it in to the output
adds r0, r0, r5 // OR in the mantissa
pop {r4,r5,r6,pc} // and return
LOCAL_LABEL(rte):
// Out-of-line handler for the round-to-even case. Clear the low mantissa bit
// and go back to the post-rounding code.
movs r5, #1
bics r0, r0, r5
b LOCAL_LABEL(rounded)
LOCAL_LABEL(outflow):
cmp r2, #0
bgt LOCAL_LABEL(overflow)
// To handle underflow, we construct an intermediate value in the IEEE 754
// style (using our existing full-length mantissa, and bias the exponent by
// +0xC0), and indicate whether that intermediate was rounded up, down or not
// at all. Then call the helper function funder, which will denormalise and
// re-round correctly.
lsls r1, r0, #7 // shift up the post-rounding mantissa
subs r1, r3, r1 // and subtract it from the pre-rounding version
lsls r6, r6, #15
cmp r6, #1 // if the rest of the low bits are nonzero
adcs r1, r1, r1 // then set an extra bit at the bottom
lsls r4, r4, #31
orrs r0, r0, r4 // put on the sign
adds r2, r2, #192 // bias the exponent
lsls r3, r2, #23
adds r0, r0, r3 // put on the biased exponent
bl SYMBOL_NAME(__compiler_rt_funder)
pop {r4,r5,r6,pc}
LOCAL_LABEL(overflow):
// Handle overflow by returning an infinity of the correct sign.
lsls r4, r4, #8 // move the sign up to bit 8
movs r0, #0xff
orrs r0, r0, r4 // fill in an exponent just below it
lsls r0, r0, #23 // and shift those 9 bits up to the top of the word
pop {r4,r5,r6,pc}
// We come here if there's at least one zero or denormal. On the fast path
// above, it was convenient to check these before checking NaNs and
// infinities, but NaNs take precedence, so now we're off the fast path, we
// must still check for those.
//
// At the main entry point 'zerodenorm' we want r2 and r3 to be the two input
// exponents. So if we branched after shifting-and-checking r2, we come to
// this earlier entry point 'zerodenorm0' so that we still shift r3.
LOCAL_LABEL(zerodenorm0):
lsrs r3, r3, #24
LOCAL_LABEL(zerodenorm):
cmp r2, #255
beq LOCAL_LABEL(naninf)
cmp r3, #255
beq LOCAL_LABEL(naninf)
// Now we know we have at least one zero or denormal, and no NaN or infinity.
// Check if either input is actually zero. We've ruled out 0 * infinity by
// this point, so any zero input means we return zero of the correct sign.
lsls r6, r0, #1 // is one input zero?
beq LOCAL_LABEL(zero) // yes, go and return zero
lsls r6, r1, #1 // is the other one zero?
bne LOCAL_LABEL(denorm) // if not, one must have been a denormal
LOCAL_LABEL(zero):
lsls r0, r4, #31 // shift up the output sign to make the return value
pop {r4,r5,r6,pc}
// Handle denormals via the helper function fnorm2, which will break both
// inputs up into mantissa and exponent, renormalising and generating a
// negative exponent if necessary.
LOCAL_LABEL(denorm):
push {r0,r1,r2,r3}
mov r0, sp
bl SYMBOL_NAME(__compiler_rt_fnorm2)
pop {r0,r1,r2,r3}
// Convert fnorm2's return values into the right form to rejoin the main
// code path.
lsls r0, r0, #1
lsls r1, r1, #1
adds r2, r2, r3
subs r2, r2, #0x7f
b LOCAL_LABEL(normalised)
// We come here if at least one input is a NaN or infinity. There may still
// be zeroes (or denormals, though they make no difference at this stage).
LOCAL_LABEL(naninf):
movs r6, #0xff
lsls r6, r6, #24
lsls r5, r0, #1
cmp r5, r6
bhi LOCAL_LABEL(nan) // first operand is a NaN
lsls r5, r1, #1
cmp r5, r6
bhi LOCAL_LABEL(nan) // second operand is a NaN
// We know we have at least one infinity, and no NaNs. We might also have a
// zero, in which case we return the default quiet NaN.
lsls r6, r0, #1
beq LOCAL_LABEL(infzero) // if r0 is a zero, r1 must be inf
lsls r6, r1, #1
beq LOCAL_LABEL(infzero) // if r1 is a zero, r0 must be inf
// Otherwise we have infinity * infinity, or infinity * finite. Just return
// an appropriately signed infinity.
b LOCAL_LABEL(overflow) // reuse the code there
// We come here if at least one input is a NaN. Hand off to fnan2, which
// propagates an appropriate NaN to the output, dealing with the special
// cases of signalling/quiet NaNs.
LOCAL_LABEL(nan):
bl SYMBOL_NAME(__compiler_rt_fnan2)
pop {r4,r5,r6,pc}
// Return a quiet NaN as the result of infinity * zero.
LOCAL_LABEL(infzero):
ldr r0, =0x7fc00000
pop {r4,r5,r6,pc}
END_COMPILERRT_FUNCTION(__mulsf3)
NO_EXEC_STACK_DIRECTIVE