// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT-0

// ----------------------------------------------------------------------------
// Montgomery inverse modulo p_256 = 2^256 - 2^224 + 2^192 + 2^96 - 1
// Input x[4]; output z[4]
//
// extern void bignum_montinv_p256(uint64_t z[static 4],uint64_t x[static 4]);
//
// If the 4-digit input x is coprime to p_256, i.e. is not divisible
// by it, returns z < p_256 such that x * z == 2^512 (mod p_256). This
// is effectively "Montgomery inverse" because if we consider x and z as
// Montgomery forms of X and Z, i.e. x == 2^256 * X and z == 2^256 * Z
// (both mod p_256) then X * Z == 1 (mod p_256). That is, this function
// gives the analog of the modular inverse bignum_inv_p256 but with both
// input and output in the Montgomery domain. Note that x does not need
// to be reduced modulo p_256, but the output always is. If the input
// is divisible (i.e. is 0 or p_256), then there can be no solution to
// the congruence x * z == 2^512 (mod p_256), and z = 0 is returned.
//
// Standard ARM ABI: X0 = z, X1 = x
// ----------------------------------------------------------------------------
#include "_internal_s2n_bignum.h"

        S2N_BN_SYM_VISIBILITY_DIRECTIVE(bignum_montinv_p256)
        S2N_BN_SYM_PRIVACY_DIRECTIVE(bignum_montinv_p256)

        .text
        .balign 4

// Size in bytes of a 64-bit word

#define N 8

// Used for the return pointer

#define res x20

// Loop counter and d = 2 * delta value for divstep

#define i x21
#define d x22

// Registers used for matrix element magnitudes and signs

#define m00 x10
#define m01 x11
#define m10 x12
#define m11 x13
#define s00 x14
#define s01 x15
#define s10 x16
#define s11 x17

// Initial carries for combinations

#define car0 x9
#define car1 x19

// Input and output, plain registers treated according to pattern

#define reg0 x0, #0
#define reg1 x1, #0
#define reg2 x2, #0
#define reg3 x3, #0
#define reg4 x4, #0

#define x x1, #0
#define z x0, #0

// Pointer-offset pairs for temporaries on stack

#define f sp, #0
#define g sp, #(6*N)
#define u sp, #(12*N)
#define v sp, #(16*N)

// Total size to reserve on the stack

#define NSPACE #(20*N)

// Loading large constants

#define movbig(nn,n3,n2,n1,n0)                                      \
        movz    nn, n0;                                             \
        movk    nn, n1, lsl #16;                                    \
        movk    nn, n2, lsl #32;                                    \
        movk    nn, n3, lsl #48

// ---------------------------------------------------------------------------
// Core signed almost-Montgomery reduction macro. Takes input in
// [d4;d3;d2;d1;d0] and returns result in [d4;d3;d2;d1], adding to
// the existing [d4;d3;d2;d1], and re-using d0 as a temporary internally
// as well as t0, t1, t2. This is almost-Montgomery, i.e. the result fits
// in 4 digits but is not necessarily strictly reduced mod p_256.
// ---------------------------------------------------------------------------

#define amontred(d4,d3,d2,d1,d0, t2,t1,t0)                                  \
/* We only know the input is -2^316 < x < 2^316. To do traditional  */      \
/* unsigned Montgomery reduction, start by adding 2^61 * p_256.     */      \
        mov     t0, #0xe000000000000000;                            \
        adds    d0, d0, t0;                                         \
        sbcs    d1, d1, xzr;                                        \
        mov     t1, #0x000000001fffffff;                            \
        adcs    d2, d2, t1;                                         \
        mov     t2, #0x2000000000000000;                            \
        adcs    d3, d3, t2;                                         \
        mov     t0, #0x1fffffffe0000000;                            \
        adc     d4, d4, t0;                                         \
/* Let w = d0, the original word we use as offset; d0 gets recycled */      \
/* First let [t2;t1] = 2^32 * w                                     */      \
/* then let [d0;t0] = (2^64 - 2^32 + 1) * w (overwrite old d0)      */      \
        lsl     t1, d0, #32;                                        \
        subs    t0, d0, t1;                                         \
        lsr     t2, d0, #32;                                        \
        sbc     d0, d0, t2;                                         \
/* Hence basic [d4;d3;d2;d1] += (2^256 - 2^224 + 2^192 + 2^96) * w  */      \
        adds    d1, d1, t1;                                         \
        adcs    d2, d2, t2;                                         \
        adcs    d3, d3, t0;                                         \
        adcs    d4, d4, d0;                                         \
/* Now capture top carry and subtract p_256 if set (almost-Montgomery) */   \
        mov     t0, #0xffffffffffffffff;                            \
        mov     t1, #0x00000000ffffffff;                            \
        mov     t2, #0xffffffff00000001;                            \
        csel    t0, t0, xzr, cs;                                    \
        csel    t1, t1, xzr, cs;                                    \
        csel    t2, t2, xzr, cs;                                    \
        subs    d1, d1, t0;                                         \
        sbcs    d2, d2, t1;                                         \
        sbcs    d3, d3, xzr;                                        \
        sbc     d4, d4, t2

// Very similar to a subroutine call to the s2n-bignum word_divstep59.
// But different in register usage and returning the final matrix in
// registers as follows
//
// [ m00  m01]
// [ m10  m11]

#define divstep59()                                                     \
        and     x4, x2, #0xfffff;                                       \
        orr     x4, x4, #0xfffffe0000000000;                            \
        and     x5, x3, #0xfffff;                                       \
        orr     x5, x5, #0xc000000000000000;                            \
        tst     x5, #0x1;                                               \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        asr     x5, x5, #1;                                             \
        add     x8, x4, #0x100, lsl #12;                                \
        sbfx    x8, x8, #21, #21;                                       \
        mov     x11, #0x100000;                                         \
        add     x11, x11, x11, lsl #21;                                 \
        add     x9, x4, x11;                                            \
        asr     x9, x9, #42;                                            \
        add     x10, x5, #0x100, lsl #12;                               \
        sbfx    x10, x10, #21, #21;                                     \
        add     x11, x5, x11;                                           \
        asr     x11, x11, #42;                                          \
        mul     x6, x8, x2;                                             \
        mul     x7, x9, x3;                                             \
        mul     x2, x10, x2;                                            \
        mul     x3, x11, x3;                                            \
        add     x4, x6, x7;                                             \
        add     x5, x2, x3;                                             \
        asr     x2, x4, #20;                                            \
        asr     x3, x5, #20;                                            \
        and     x4, x2, #0xfffff;                                       \
        orr     x4, x4, #0xfffffe0000000000;                            \
        and     x5, x3, #0xfffff;                                       \
        orr     x5, x5, #0xc000000000000000;                            \
        tst     x5, #0x1;                                               \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        asr     x5, x5, #1;                                             \
        add     x12, x4, #0x100, lsl #12;                               \
        sbfx    x12, x12, #21, #21;                                     \
        mov     x15, #0x100000;                                         \
        add     x15, x15, x15, lsl #21;                                 \
        add     x13, x4, x15;                                           \
        asr     x13, x13, #42;                                          \
        add     x14, x5, #0x100, lsl #12;                               \
        sbfx    x14, x14, #21, #21;                                     \
        add     x15, x5, x15;                                           \
        asr     x15, x15, #42;                                          \
        mul     x6, x12, x2;                                            \
        mul     x7, x13, x3;                                            \
        mul     x2, x14, x2;                                            \
        mul     x3, x15, x3;                                            \
        add     x4, x6, x7;                                             \
        add     x5, x2, x3;                                             \
        asr     x2, x4, #20;                                            \
        asr     x3, x5, #20;                                            \
        and     x4, x2, #0xfffff;                                       \
        orr     x4, x4, #0xfffffe0000000000;                            \
        and     x5, x3, #0xfffff;                                       \
        orr     x5, x5, #0xc000000000000000;                            \
        tst     x5, #0x1;                                               \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        mul     x2, x12, x8;                                            \
        mul     x3, x12, x9;                                            \
        mul     x6, x14, x8;                                            \
        mul     x7, x14, x9;                                            \
        madd    x8, x13, x10, x2;                                       \
        madd    x9, x13, x11, x3;                                       \
        madd    x16, x15, x10, x6;                                      \
        madd    x17, x15, x11, x7;                                      \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        tst     x5, #0x2;                                               \
        asr     x5, x5, #1;                                             \
        csel    x6, x4, xzr, ne;                                        \
        ccmp    x1, xzr, #0x8, ne;                                      \
        cneg    x1, x1, ge;                                             \
        cneg    x6, x6, ge;                                             \
        csel    x4, x5, x4, ge;                                         \
        add     x5, x5, x6;                                             \
        add     x1, x1, #0x2;                                           \
        asr     x5, x5, #1;                                             \
        add     x12, x4, #0x100, lsl #12;                               \
        sbfx    x12, x12, #22, #21;                                     \
        mov     x15, #0x100000;                                         \
        add     x15, x15, x15, lsl #21;                                 \
        add     x13, x4, x15;                                           \
        asr     x13, x13, #43;                                          \
        add     x14, x5, #0x100, lsl #12;                               \
        sbfx    x14, x14, #22, #21;                                     \
        add     x15, x5, x15;                                           \
        asr     x15, x15, #43;                                          \
        mneg    x2, x12, x8;                                            \
        mneg    x3, x12, x9;                                            \
        mneg    x4, x14, x8;                                            \
        mneg    x5, x14, x9;                                            \
        msub    m00, x13, x16, x2;                                      \
        msub    m01, x13, x17, x3;                                      \
        msub    m10, x15, x16, x4;                                      \
        msub    m11, x15, x17, x5

S2N_BN_SYMBOL(bignum_montinv_p256):

// Save registers and make room for temporaries

        stp     x19, x20, [sp, -16]!
        stp     x21, x22, [sp, -16]!
        stp     x23, x24, [sp, -16]!
        sub     sp, sp, NSPACE

// Save the return pointer for the end so we can overwrite x0 later

        mov     res, x0

// Copy the prime and input into the main f and g variables respectively.
// Make sure x is reduced so that g <= f as assumed in the bound proof.

        mov     x10, #0xffffffffffffffff
        mov     x11, #0x00000000ffffffff
        mov     x13, #0xffffffff00000001
        stp     x10, x11, [f]
        stp     xzr, x13, [f+2*N]
        str     xzr, [f+4*N]

        ldp     x2, x3, [x1]
        subs    x10, x2, x10
        sbcs    x11, x3, x11
        ldp     x4, x5, [x1, #(2*N)]
        sbcs    x12, x4, xzr
        sbcs    x13, x5, x13

        csel    x2, x2, x10, cc
        csel    x3, x3, x11, cc
        csel    x4, x4, x12, cc
        csel    x5, x5, x13, cc

        stp     x2, x3, [g]
        stp     x4, x5, [g+2*N]
        str     xzr, [g+4*N]

// Also maintain reduced < 2^256 vector [u,v] such that
// [f,g] == x * 2^{5*i-562} * [u,v] (mod p_256)
// starting with [p_256,x] == x * 2^{5*0-562} * [0,2^562] (mod p_256)
// The weird-looking 5*i modifications come in because we are doing
// 64-bit word-sized Montgomery reductions at each stage, which is
// 5 bits more than the 59-bit requirement to keep things stable.
// After the 10th and last iteration and sign adjustment, when
// f == 1 for in-scope cases, we have x * 2^{50-562} * u == 1, i.e.
// x * u == 2^512 as required.

        stp     xzr, xzr, [u]
        stp     xzr, xzr, [u+2*N]

// The starting constant 2^562 mod p_256 is
// 0x000bffffffebffff:fffbffffffefffff:ffe8000000000000:000c000000140000
// where colons separate 64-bit subwords, least significant at the right.
// Only word number 1, value 0xffe8000000000000, is a single ARM move.

        mov     x10, #0x0000000000140000
        orr     x10, x10, #0x000c000000000000

        mov     x11, #0xffe8000000000000

        movbig(x13, #0x000b, #0xffff, #0xffef, #0xffff)
        orr     x12, x13, #0xfff0000000000000
        and     x13, x13, #0xfffffffffffbffff

        stp     x10, x11, [v]
        stp     x12, x13, [v+2*N]

// Start of main loop. We jump into the middle so that the divstep
// portion is common to the special tenth iteration after a uniform
// first 9.

        mov     i, #10
        mov     d, #1
        b       bignum_montinv_p256_midloop

bignum_montinv_p256_loop:

// Separate the matrix elements into sign-magnitude pairs

        cmp     m00, xzr
        csetm   s00, mi
        cneg    m00, m00, mi

        cmp     m01, xzr
        csetm   s01, mi
        cneg    m01, m01, mi

        cmp     m10, xzr
        csetm   s10, mi
        cneg    m10, m10, mi

        cmp     m11, xzr
        csetm   s11, mi
        cneg    m11, m11, mi

// Adjust the initial values to allow for complement instead of negation
// This initial offset is the same for [f,g] and [u,v] compositions.
// Save it in stable registers for the [u,v] part and do [f,g] first.

        and     x0, m00, s00
        and     x1, m01, s01
        add     car0, x0, x1

        and     x0, m10, s10
        and     x1, m11, s11
        add     car1, x0, x1

// Now the computation of the updated f and g values. This maintains a
// 2-word carry between stages so we can conveniently insert the shift
// right by 59 before storing back, and not overwrite digits we need
// again of the old f and g values.
//
// Digit 0 of [f,g]

        ldr     x7, [f]
        eor     x1, x7, s00
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x4, car0, x0
        adc     x2, xzr, x1
        ldr     x8, [g]
        eor     x1, x8, s01
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x4, x4, x0
        adc     x2, x2, x1

        eor     x1, x7, s10
        mul     x0, x1, m10
        umulh   x1, x1, m10
        adds    x5, car1, x0
        adc     x3, xzr, x1
        eor     x1, x8, s11
        mul     x0, x1, m11
        umulh   x1, x1, m11
        adds    x5, x5, x0
        adc     x3, x3, x1

// Digit 1 of [f,g]

        ldr     x7, [f+N]
        eor     x1, x7, s00
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x2, x2, x0
        adc     x6, xzr, x1
        ldr     x8, [g+N]
        eor     x1, x8, s01
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x2, x2, x0
        adc     x6, x6, x1
        extr    x4, x2, x4, #59
        str     x4, [f]

        eor     x1, x7, s10
        mul     x0, x1, m10
        umulh   x1, x1, m10
        adds    x3, x3, x0
        adc     x4, xzr, x1
        eor     x1, x8, s11
        mul     x0, x1, m11
        umulh   x1, x1, m11
        adds    x3, x3, x0
        adc     x4, x4, x1
        extr    x5, x3, x5, #59
        str     x5, [g]

// Digit 2 of [f,g]

        ldr     x7, [f+2*N]
        eor     x1, x7, s00
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x6, x6, x0
        adc     x5, xzr, x1
        ldr     x8, [g+2*N]
        eor     x1, x8, s01
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x6, x6, x0
        adc     x5, x5, x1
        extr    x2, x6, x2, #59
        str     x2, [f+N]

        eor     x1, x7, s10
        mul     x0, x1, m10
        umulh   x1, x1, m10
        adds    x4, x4, x0
        adc     x2, xzr, x1
        eor     x1, x8, s11
        mul     x0, x1, m11
        umulh   x1, x1, m11
        adds    x4, x4, x0
        adc     x2, x2, x1
        extr    x3, x4, x3, #59
        str     x3, [g+N]

// Digits 3 and 4 of [f,g]

        ldr     x7, [f+3*N]
        eor     x1, x7, s00
        ldr     x23, [f+4*N]
        eor     x3, x23, s00
        and     x3, x3, m00
        neg     x3, x3
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x5, x5, x0
        adc     x3, x3, x1
        ldr     x8, [g+3*N]
        eor     x1, x8, s01
        ldr     x24, [g+4*N]
        eor     x0, x24, s01
        and     x0, x0, m01
        sub     x3, x3, x0
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x5, x5, x0
        adc     x3, x3, x1
        extr    x6, x5, x6, #59
        str     x6, [f+2*N]
        extr    x5, x3, x5, #59
        str     x5, [f+3*N]
        asr     x3, x3, #59
        str     x3, [f+4*N]

        eor     x1, x7, s10
        eor     x5, x23, s10
        and     x5, x5, m10
        neg     x5, x5
        mul     x0, x1, m10
        umulh   x1, x1, m10
        adds    x2, x2, x0
        adc     x5, x5, x1
        eor     x1, x8, s11
        eor     x0, x24, s11
        and     x0, x0, m11
        sub     x5, x5, x0
        mul     x0, x1, m11
        umulh   x1, x1, m11
        adds    x2, x2, x0
        adc     x5, x5, x1
        extr    x4, x2, x4, #59
        str     x4, [g+2*N]
        extr    x2, x5, x2, #59
        str     x2, [g+3*N]
        asr     x5, x5, #59
        str     x5, [g+4*N]

// Now the computation of the updated u and v values and their
// Montgomery reductions. A very similar accumulation except that
// the top words of u and v are unsigned and we don't shift.
//
// Digit 0 of [u,v]

        ldr     x7, [u]
        eor     x1, x7, s00
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x4, car0, x0
        adc     x2, xzr, x1
        ldr     x8, [v]
        eor     x1, x8, s01
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x4, x4, x0
        str     x4, [u]
        adc     x2, x2, x1

        eor     x1, x7, s10
        mul     x0, x1, m10
        umulh   x1, x1, m10
        adds    x5, car1, x0
        adc     x3, xzr, x1
        eor     x1, x8, s11
        mul     x0, x1, m11
        umulh   x1, x1, m11
        adds    x5, x5, x0
        str     x5, [v]
        adc     x3, x3, x1

// Digit 1 of [u,v]

        ldr     x7, [u+N]
        eor     x1, x7, s00
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x2, x2, x0
        adc     x6, xzr, x1
        ldr     x8, [v+N]
        eor     x1, x8, s01
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x2, x2, x0
        str     x2, [u+N]
        adc     x6, x6, x1

        eor     x1, x7, s10
        mul     x0, x1, m10
        umulh   x1, x1, m10
        adds    x3, x3, x0
        adc     x4, xzr, x1
        eor     x1, x8, s11
        mul     x0, x1, m11
        umulh   x1, x1, m11
        adds    x3, x3, x0
        str     x3, [v+N]
        adc     x4, x4, x1

// Digit 2 of [u,v]

        ldr     x7, [u+2*N]
        eor     x1, x7, s00
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x6, x6, x0
        adc     x5, xzr, x1
        ldr     x8, [v+2*N]
        eor     x1, x8, s01
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x6, x6, x0
        str     x6, [u+2*N]
        adc     x5, x5, x1

        eor     x1, x7, s10
        mul     x0, x1, m10
        umulh   x1, x1, m10
        adds    x4, x4, x0
        adc     x2, xzr, x1
        eor     x1, x8, s11
        mul     x0, x1, m11
        umulh   x1, x1, m11
        adds    x4, x4, x0
        str     x4, [v+2*N]
        adc     x2, x2, x1

// Digits 3 and 4 of u (top is unsigned)

        ldr     x7, [u+3*N]
        eor     x1, x7, s00
        and     x3, s00, m00
        neg     x3, x3
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x5, x5, x0
        adc     x3, x3, x1
        ldr     x8, [v+3*N]
        eor     x1, x8, s01
        and     x0, s01, m01
        sub     x3, x3, x0
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x5, x5, x0
        adc     x3, x3, x1

// Montgomery reduction of u

        ldp     x0, x1, [u]
        ldr     x6, [u+2*N]
        amontred(x3,x5,x6,x1,x0, x10,x11,x14)
        stp     x1, x6, [u]
        stp     x5, x3, [u+16]

// Digits 3 and 4 of v (top is unsigned)

        eor     x1, x7, s10
        and     x5, s10, m10
        neg     x5, x5
        mul     x0, x1, m10
        umulh   x1, x1, m10
        adds    x2, x2, x0
        adc     x5, x5, x1
        eor     x1, x8, s11
        and     x0, s11, m11
        sub     x5, x5, x0
        mul     x0, x1, m11
        umulh   x1, x1, m11
        adds    x2, x2, x0
        adc     x5, x5, x1

// Montgomery reduction of v

        ldp     x0, x1, [v]
        ldr     x3, [v+2*N]
        amontred(x5,x2,x3,x1,x0, x10,x11,x14)
        stp     x1, x3, [v]
        stp     x2, x5, [v+16]

bignum_montinv_p256_midloop:

        mov     x1, d
        ldr     x2, [f]
        ldr     x3, [g]
        divstep59()
        mov     d, x1

// Next iteration

        subs    i, i, #1
        bne     bignum_montinv_p256_loop

// The 10th and last iteration does not need anything except the
// u value and the sign of f; the latter can be obtained from the
// lowest word of f. So it's done differently from the main loop.
// Find the sign of the new f. For this we just need one digit
// since we know (for in-scope cases) that f is either +1 or -1.
// We don't explicitly shift right by 59 either, but looking at
// bit 63 (or any bit >= 60) of the unshifted result is enough
// to distinguish -1 from +1; this is then made into a mask.

        ldr     x0, [f]
        ldr     x1, [g]
        mul     x0, x0, m00
        madd    x1, x1, m01, x0
        asr     x0, x1, #63

// Now separate out the matrix into sign-magnitude pairs
// and adjust each one based on the sign of f.
//
// Note that at this point we expect |f|=1 and we got its
// sign above, so then since [f,0] == x * 2^{-512} [u,v] (mod p_256)
// we want to flip the sign of u according to that of f.

        cmp     m00, xzr
        csetm   s00, mi
        cneg    m00, m00, mi
        eor     s00, s00, x0

        cmp     m01, xzr
        csetm   s01, mi
        cneg    m01, m01, mi
        eor     s01, s01, x0

        cmp     m10, xzr
        csetm   s10, mi
        cneg    m10, m10, mi
        eor     s10, s10, x0

        cmp     m11, xzr
        csetm   s11, mi
        cneg    m11, m11, mi
        eor     s11, s11, x0

// Adjust the initial value to allow for complement instead of negation

        and     x0, m00, s00
        and     x1, m01, s01
        add     car0, x0, x1

// Digit 0 of [u]

        ldr     x7, [u]
        eor     x1, x7, s00
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x4, car0, x0
        adc     x2, xzr, x1
        ldr     x8, [v]
        eor     x1, x8, s01
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x4, x4, x0
        str     x4, [u]
        adc     x2, x2, x1

// Digit 1 of [u]

        ldr     x7, [u+N]
        eor     x1, x7, s00
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x2, x2, x0
        adc     x6, xzr, x1
        ldr     x8, [v+N]
        eor     x1, x8, s01
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x2, x2, x0
        str     x2, [u+N]
        adc     x6, x6, x1

// Digit 2 of [u]

        ldr     x7, [u+2*N]
        eor     x1, x7, s00
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x6, x6, x0
        adc     x5, xzr, x1
        ldr     x8, [v+2*N]
        eor     x1, x8, s01
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x6, x6, x0
        str     x6, [u+2*N]
        adc     x5, x5, x1

// Digits 3 and 4 of u (top is unsigned)

        ldr     x7, [u+3*N]
        eor     x1, x7, s00
        and     x3, s00, m00
        neg     x3, x3
        mul     x0, x1, m00
        umulh   x1, x1, m00
        adds    x5, x5, x0
        adc     x3, x3, x1
        ldr     x8, [v+3*N]
        eor     x1, x8, s01
        and     x0, s01, m01
        sub     x3, x3, x0
        mul     x0, x1, m01
        umulh   x1, x1, m01
        adds    x5, x5, x0
        adc     x3, x3, x1

// Montgomery reduction of u. This needs to be strict not "almost"
// so it is followed by an optional subtraction of p_256

        ldp     x0, x1, [u]
        ldr     x2, [u+2*N]
        amontred(x3,x5,x2,x1,x0, x10,x11,x14)

        mov     x10, #0xffffffffffffffff
        subs    x10, x1, x10
        mov     x11, #0x00000000ffffffff
        sbcs    x11, x2, x11
        mov     x13, #0xffffffff00000001
        sbcs    x12, x5, xzr
        sbcs    x13, x3, x13

        csel    x10, x1, x10, cc
        csel    x11, x2, x11, cc
        csel    x12, x5, x12, cc
        csel    x13, x3, x13, cc

// Store it back to the final output

        stp     x10, x11, [res]
        stp     x12, x13, [res, #16]

// Restore stack and registers

        add     sp, sp, NSPACE
        ldp     x23, x24, [sp], 16
        ldp     x21, x22, [sp], 16
        ldp     x19, x20, [sp], 16
        ret

#if defined(__linux__) && defined(__ELF__)
.section .note.GNU-stack, "", %progbits
#endif
