//===-- addsf3.S - Adds two single precision floating pointer numbers-----===//
//
// 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 the __addsf3 (single precision floating pointer number
// addition with the IEEE-754 default rounding (to nearest, ties to even)
// function for the ARM Thumb1 ISA.
//
//===----------------------------------------------------------------------===//

#include "../assembly.h"
#define significandBits 23
#define typeWidth 32

	.syntax unified
	.text
  .thumb
  .p2align 2

DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_fadd, __addsf3)

DEFINE_COMPILERRT_THUMB_FUNCTION(__addsf3)
  push {r4, r5, r6, r7, lr}
  // Get the absolute value of a and b.
  lsls r2, r0, #1
  lsls r3, r1, #1
  lsrs r2, r2, #1  // aAbs
  beq  LOCAL_LABEL(a_zero_nan_inf)
  lsrs r3, r3, #1  // bAbs
  beq  LOCAL_LABEL(zero_nan_inf)

  // Detect if a or b is infinity or Nan.
  lsrs r6, r2, #(significandBits)
  lsrs r7, r3, #(significandBits)
  cmp  r6, #0xFF
  beq  LOCAL_LABEL(zero_nan_inf)
  cmp  r7, #0xFF
  beq  LOCAL_LABEL(zero_nan_inf)

  // Swap Rep and Abs so that a and aAbs has the larger absolute value.
  cmp r2, r3
  bhs LOCAL_LABEL(no_swap)
  movs r4, r0
  movs r5, r2
  movs r0, r1
  movs r2, r3
  movs r1, r4
  movs r3, r5
LOCAL_LABEL(no_swap):

  // Get the significands and shift them to give us round, guard and sticky.
  lsls r4, r0, #(typeWidth - significandBits)
  lsrs r4, r4, #(typeWidth - significandBits - 3) // aSignificand << 3
  lsls r5, r1, #(typeWidth - significandBits)
  lsrs r5, r5, #(typeWidth - significandBits - 3) // bSignificand << 3

  // Get the implicitBit.
  movs r6, #1
  lsls r6, r6, #(significandBits + 3)

  // Get aExponent and set implicit bit if necessary.
  lsrs r2, r2, #(significandBits)
  beq LOCAL_LABEL(a_done_implicit_bit)
  orrs r4, r6
LOCAL_LABEL(a_done_implicit_bit):

  // Get bExponent and set implicit bit if necessary.
  lsrs r3, r3, #(significandBits)
  beq LOCAL_LABEL(b_done_implicit_bit)
  orrs r5, r6
LOCAL_LABEL(b_done_implicit_bit):

  // Get the difference in exponents.
  subs r6, r2, r3
  beq LOCAL_LABEL(done_align)

  // If b is denormal, then a must be normal as align > 0, and we only need to
  // right shift bSignificand by (align - 1) bits.
  cmp  r3, #0
  bne  1f
  subs r6, r6, #1
1:

  // No longer needs bExponent. r3 is dead here.
  // Set sticky bits of b: sticky = bSignificand << (typeWidth - align).
  movs r3, #(typeWidth)
  subs r3, r3, r6
  movs r7, r5
  lsls r7, r3
  beq 1f
  movs r7, #1
1:

  // bSignificand = bSignificand >> align | sticky;
  lsrs r5, r6
  orrs r5, r7
  bne LOCAL_LABEL(done_align)
  movs r5, #1 //  sticky; b is known to be non-zero.

LOCAL_LABEL(done_align):
  // isSubtraction = (aRep ^ bRep) >> 31;
  movs r7, r0
  eors r7, r1
  lsrs r7, #31
  bne LOCAL_LABEL(do_substraction)

  // Same sign, do Addition.

  // aSignificand += bSignificand;
  adds r4, r4, r5

  // Check carry bit.
  movs r6, #1
  lsls r6, r6, #(significandBits + 3 + 1)
  movs r7, r4
  ands r7, r6
  beq LOCAL_LABEL(form_result)
  // If the addition carried up, we need to right-shift the result and
  // adjust the exponent.
  movs r7, r4
  movs r6, #1
  ands r7, r6 // sticky = aSignificand & 1;
  lsrs r4, #1
  orrs r4, r7  // result Significand
  adds r2, #1  // result Exponent
  // If we have overflowed the type, return +/- infinity.
  cmp  r2, 0xFF
  beq  LOCAL_LABEL(ret_inf)

LOCAL_LABEL(form_result):
  // Shift the sign, exponent and significand into place.
  lsrs r0, #(typeWidth - 1)
  lsls r0, #(typeWidth - 1) // Get Sign.
  lsls r2, #(significandBits)
  orrs r0, r2
  movs r1, r4
  lsls r4, #(typeWidth - significandBits - 3)
  lsrs r4, #(typeWidth - significandBits)
  orrs r0, r4

  // Final rounding.  The result may overflow to infinity, but that is the
  // correct result in that case.
  // roundGuardSticky = aSignificand & 0x7;
  movs r2, #0x7
  ands r1, r2
  // if (roundGuardSticky > 0x4) result++;

  cmp r1, #0x4
  blt LOCAL_LABEL(done_round)
  beq 1f
  adds r0, #1
  pop {r4, r5, r6, r7, pc}
1:

  // if (roundGuardSticky == 0x4) result += result & 1;
  movs r1, r0
  lsrs r1, #1
  bcc  LOCAL_LABEL(done_round)
  adds r0, r0, #1
LOCAL_LABEL(done_round):
  pop {r4, r5, r6, r7, pc}

LOCAL_LABEL(do_substraction):
  subs r4, r4, r5 // aSignificand -= bSignificand;
  beq  LOCAL_LABEL(ret_zero)
  movs r6, r4
  cmp  r2, 0
  beq  LOCAL_LABEL(form_result) // if a's exp is 0, no need to normalize.
  // If partial cancellation occured, we need to left-shift the result
  // and adjust the exponent:
  lsrs r6, r6, #(significandBits + 3)
  bne LOCAL_LABEL(form_result)

  push {r0, r1, r2, r3}
  movs r0, r4
  bl   SYMBOL_NAME(__clzsi2)
  movs r5, r0
  pop {r0, r1, r2, r3}
  // shift = rep_clz(aSignificand) - rep_clz(implicitBit << 3);
  subs r5, r5, #(typeWidth - significandBits - 3 - 1)
  // aSignificand <<= shift; aExponent -= shift;
  lsls r4, r5
  subs  r2, r2, r5
  bgt LOCAL_LABEL(form_result)

  // Do normalization if aExponent <= 0.
  movs r6, #1
  subs r6, r6, r2 // 1 - aExponent;
  movs r2, #0 // aExponent = 0;
  movs r3, #(typeWidth) // bExponent is dead.
  subs r3, r3, r6
  movs r7, r4
  lsls r7, r3  // stickyBit = (bool)(aSignificant << (typeWidth - align))
  beq 1f
  movs r7, #1
1:
  lsrs r4, r6 // aSignificand >> shift
  orrs r4, r7
  b LOCAL_LABEL(form_result)

LOCAL_LABEL(ret_zero):
  movs r0, #0
  pop {r4, r5, r6, r7, pc}


LOCAL_LABEL(a_zero_nan_inf):
  lsrs r3, r3, #1

LOCAL_LABEL(zero_nan_inf):
  // Here  r2 has aAbs, r3 has bAbs
  movs r4, #0xFF
  lsls r4, r4, #(significandBits) // Make +inf.

  cmp r2, r4
  bhi LOCAL_LABEL(a_is_nan)
  cmp r3, r4
  bhi LOCAL_LABEL(b_is_nan)

  cmp r2, r4
  bne LOCAL_LABEL(a_is_rational)
  // aAbs is INF.
  eors r1, r0 // aRep ^ bRep.
  movs r6, #1
  lsls r6, r6, #(typeWidth - 1) // get sign mask.
  cmp r1, r6 // if they only differ on sign bit, it's -INF + INF
  beq LOCAL_LABEL(a_is_nan)
  pop {r4, r5, r6, r7, pc}

LOCAL_LABEL(a_is_rational):
  cmp r3, r4
  bne LOCAL_LABEL(b_is_rational)
  movs r0, r1
  pop {r4, r5, r6, r7, pc}

LOCAL_LABEL(b_is_rational):
  // either a or b or both are zero.
  adds r4, r2, r3
  beq  LOCAL_LABEL(both_zero)
  cmp r2, #0 // is absA 0 ?
  beq LOCAL_LABEL(ret_b)
  pop {r4, r5, r6, r7, pc}

LOCAL_LABEL(both_zero):
  ands r0, r1 // +0 + -0 = +0
  pop {r4, r5, r6, r7, pc}

LOCAL_LABEL(ret_b):
  movs r0, r1

LOCAL_LABEL(ret):
  pop {r4, r5, r6, r7, pc}

LOCAL_LABEL(b_is_nan):
  movs r0, r1
LOCAL_LABEL(a_is_nan):
  movs r1, #1
  lsls r1, r1, #(significandBits -1) // r1 is quiet bit.
  orrs r0, r1
  pop {r4, r5, r6, r7, pc}

LOCAL_LABEL(ret_inf):
  movs r4, #0xFF
  lsls r4, r4, #(significandBits)
  orrs r0, r4
  lsrs r0, r0, #(significandBits)
  lsls r0, r0, #(significandBits)
  pop {r4, r5, r6, r7, pc}


END_COMPILERRT_FUNCTION(__addsf3)

NO_EXEC_STACK_DIRECTIVE
