/*
 * This file is part of the openHiTLS project.
 *
 * openHiTLS is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 *
 * -----------------------------------------------------------------------------
 *
 * ARMv7 assembly optimization for SM3:
 *   Contributors: Zhao Runchen, Li Xukai, Wang Weijia
 *   Affiliation: Shandong University and Quan Cheng Laboratory
 *   Date: 2025.7.10
 *
 * -----------------------------------------------------------------------------
 */

#include "hitls_build.h"
#ifdef HITLS_CRYPTO_SM3
.syntax unified
.arch armv7
.thumb

// State update function for 0~15 rounds, sp register points to the message w[i], r0 is the constant 0x79cc4519
.macro RF0 a b c d e f g h i
    LDR r1, [sp], #0x04                         // r1 = w[i]
    LDR r2, [sp, #0x0C]                         // r2 = w[i+4]
    EOR r2, r1                                  // r2 = w[i] ^ w[i+4]
    ADD \h, r1                                  // h += w[i]                (r1 is free now)
    ADD \d, r2                                  // d += w[i] ^ w[i + 4]     (r2 is free now)

	ADD r1, \e, r0, ROR #(32-\i%32)%32          // r1 = ss1 = ((a <<< 12) + e + T[i]) <<< 7
    ADD r1, r1, \a, ROR #20
    ROR r1, #25

    EOR r3, \e, \f                              // h += (e ^ f ^ g) + ss1
    EOR r3, \g
    ADD \h, r3
    ADD \h, r1

    EOR r3, \h, \h, ROR #23                     // h = h ^ (h <<< 9) ^ (h <<< 17)
    EOR \h, r3, \h, ROR #15

    EOR r2, r1, \a, ROR #20                     // r2 = ss2 = (a <<< 12) ^ ss1
    EOR r3, \a, \b                              // d += (a ^ b ^ c)+ ss2
    EOR r3, \c
    ADD \d, r3
	ADD \d, r2

    ROR \b, #23                                 // b = b <<< 9, f = f <<< 19
    ROR \f, #13
.endm

// State update function for rounds 16~63, sp register points to the message w[i], r0 is the constant 0x7a879d8a
.macro RF1 a b c d e f g h i
    LDR r1, [sp], #0x04                         // r1 = w[i]
    LDR r2, [sp, #0x0C]                         // r2 = w[i+4]
    EOR r2, r1                                  // r2 = w[i] ^ w[i+4]
    ADD \h, r1                                  // h += w[i]                (r1 is free now)
    ADD \d, r2                                  // d += w[i] ^ w[i + 4]     (r2 is free now)

	ADD r1, \e, r0, ROR #(32-\i%32)%32          // r1 = ss1 = ((a <<< 12) + e + T[i]) <<< 7
    ADD r1, r1, \a, ROR #20
    ROR r1, #25

	EOR r3, \f, \g                              // h += (e & f) | (~e & g) + ss1 = ((f ^ g) & e) ^ g + ss1
    AND r3, \e
    EOR r3, \g
    ADD \h, r3
	ADD \h, r1

    EOR r3, \h, \h, ROR #23                     // h = P0(h) = h ^ (h <<< 9) ^ (h <<< 17)
    EOR \h, r3, \h, ROR #15

    EOR r2, r1, \a, ROR #20                     // ss2 = (a <<< 12) ^ ss1   (r1 is free now)
    EOR r3, \b, \c                              // d += ((a & b) | (a & c) | (b & c)) + ss2  = (a & (b | c)) | ((b & c)) + ss2
    AND r3, \a
    AND r1, \b, \c
    EOR r3, r1
    ADD \d, r3
    ADD \d, r2

    ROR \b, #23                                 // b = b <<< 9, f = f <<< 19
    ROR \f, #13
.endm

// Message expansion: w[i+16] = P1(w[i] ^ w[i+7] ^ (w[i+13] <<< 15)) ^ (w[i+3] <<< 7) ^ w[i+10]
// P1(x) = x ^ x <<< 15 ^ x <<< 23 = x ^ x >>> 17 ^ x >>> 9
// Since the width of the sliding registers (w0-w13) is 14, there are no additional registers available for calculating P1(x).
// Therefore, w7 will be used as a temporary register here and restored from memory later.
// We hope you can have a better way to avoid reading the memory one more time.

.macro MSGEXP w0 w3 w7 w10 w13 i
    LDR \w13, [sp, #((13 + \i) << 2)]
    EOR \w0, \w0, \w7
    EOR \w0, \w0, \w13, ROR #17
    EOR \w7, \w0, \w0,  ROR #17
    EOR \w0, \w7, \w0,  ROR #9
    EOR \w0, \w0, \w3,  ROR #25
    EOR \w0, \w10
    LDR \w7, [sp, #((7 + \i) << 2)]
    STR \w0, [sp, #((16 + \i) << 2)]
.endm

// void SM3_CompressAsm(uint32_t state[8], const uint8_t *data, uint32_t blockCnt);
.globl SM3_CompressAsm
.type  SM3_CompressAsm, %function
.p2align 4
SM3_CompressAsm:
    PUSH {v1-ip, lr}
	.Lloop_start:
	SUBS r2, r2, 1
	BCC .Lloop_end
	PUSH {r0-r2}
    SUB sp, sp, #(52<<2)
    ADD r1, #0x40
    LDR v3, [r1, #-4]!
    LDR v2, [r1, #-4]!
    LDR v1, [r1, #-4]!
    REV v1, v1
    REV v2, v2
    REV v3, v3
    PUSH {v1-v3}
    LDR r12, [r1, #-4]!
    LDR r11, [r1, #-4]!
    LDR r10, [r1, #-4]!
    LDR r9, [r1, #-4]!
    LDR r8, [r1, #-4]!
    LDR r7, [r1, #-4]!
    LDR r6, [r1, #-4]!
    LDR r5, [r1, #-4]!
    LDR r4, [r1, #-4]!
    LDR r3, [r1, #-4]!
    LDR r2, [r1, #-4]!
    LDR r0, [r1, #-8]!
    LDR r1, [r1, #4]
    REV r0, r0
    REV r1, r1
    REV r2, r2
    REV r3, r3
    REV r4, r4
    REV r5, r5
    REV r6, r6
    REV r7, r7
    REV r8, r8
    REV r9, r9
    REV r10, r10
    REV r11, r11
    REV r12, r12
    PUSH {r0-r12}
    MSGEXP r0  r3  r7  r10 r14 0
    MSGEXP r1  r4  r8  r11 r0  1
    MSGEXP r2  r5  r9  r12 r1  2
    MSGEXP r3  r6  r10 r14 r2  3
    MSGEXP r4  r7  r11 r0  r3  4
    MSGEXP r5  r8  r12 r1  r4  5
    MSGEXP r6  r9  r14 r2  r5  6
    MSGEXP r7  r10 r0  r3  r6  7
    MSGEXP r8  r11 r1  r4  r7  8
    MSGEXP r9  r12 r2  r5  r8  9
    MSGEXP r10 r14 r3  r6  r9  10
    MSGEXP r11 r0  r4  r7  r10 11
    MSGEXP r12 r1  r5  r8  r11 12
    MSGEXP r14 r2  r6  r9  r12 13
    MSGEXP r0  r3  r7  r10 r14 14
    MSGEXP r1  r4  r8  r11 r0  15
    MSGEXP r2  r5  r9  r12 r1  16
    MSGEXP r3  r6  r10 r14 r2  17
    MSGEXP r4  r7  r11 r0  r3  18
    MSGEXP r5  r8  r12 r1  r4  19
    MSGEXP r6  r9  r14 r2  r5  20
    MSGEXP r7  r10 r0  r3  r6  21
    MSGEXP r8  r11 r1  r4  r7  22
    MSGEXP r9  r12 r2  r5  r8  23
    MSGEXP r10 r14 r3  r6  r9  24
    MSGEXP r11 r0  r4  r7  r10 25
    MSGEXP r12 r1  r5  r8  r11 26
    MSGEXP r14 r2  r6  r9  r12 27
    MSGEXP r0  r3  r7  r10 r14 28
    MSGEXP r1  r4  r8  r11 r0  29
    MSGEXP r2  r5  r9  r12 r1  30
    MSGEXP r3  r6  r10 r14 r2  31
    MSGEXP r4  r7  r11 r0  r3  32
    MSGEXP r5  r8  r12 r1  r4  33
    MSGEXP r6  r9  r14 r2  r5  34
    MSGEXP r7  r10 r0  r3  r6  35
    MSGEXP r8  r11 r1  r4  r7  36
    MSGEXP r9  r12 r2  r5  r8  37
    MSGEXP r10 r14 r3  r6  r9  38
    MSGEXP r11 r0  r4  r7  r10 39
    MSGEXP r12 r1  r5  r8  r11 40
    MSGEXP r14 r2  r6  r9  r12 41
    MSGEXP r0  r3  r7  r10 r14 42
    MSGEXP r1  r4  r8  r11 r0  43
    MSGEXP r2  r5  r9  r12 r1  44
    MSGEXP r3  r6  r10 r14 r2  45
    MSGEXP r4  r7  r11 r0  r3  46
    MSGEXP r5  r8  r12 r1  r4  47
    MSGEXP r6  r9  r14 r2  r5  48
    MSGEXP r7  r10 r0  r3  r6  49
    MSGEXP r8  r11 r1  r4  r7  50
    MSGEXP r9  r12 r2  r5  r8  51
    // Load the state.
    LDR r0, =0x79cc4519
    LDR r1, [sp, #(68 << 2)]
    LDM r1, {v1-v8}
    // Note: Since the LDR offset relative to the current PC value cannot exceed 4KB in ARMV7,
    // and there are approximately 2000 lines of instructions inside this function that are out of the offset range,
    // we declare the literal pool here and skip it.
    B 1f
    .ltorg
1:  // 0-15
    RF0 v1 v2 v3 v4 v5 v6 v7 v8 0
    RF0 v4 v1 v2 v3 v8 v5 v6 v7 1
    RF0 v3 v4 v1 v2 v7 v8 v5 v6 2
    RF0 v2 v3 v4 v1 v6 v7 v8 v5 3
    RF0 v1 v2 v3 v4 v5 v6 v7 v8 4
    RF0 v4 v1 v2 v3 v8 v5 v6 v7 5
    RF0 v3 v4 v1 v2 v7 v8 v5 v6 6
    RF0 v2 v3 v4 v1 v6 v7 v8 v5 7
    RF0 v1 v2 v3 v4 v5 v6 v7 v8 8
    RF0 v4 v1 v2 v3 v8 v5 v6 v7 9
    RF0 v3 v4 v1 v2 v7 v8 v5 v6 10
    RF0 v2 v3 v4 v1 v6 v7 v8 v5 11
    RF0 v1 v2 v3 v4 v5 v6 v7 v8 12
    RF0 v4 v1 v2 v3 v8 v5 v6 v7 13
    RF0 v3 v4 v1 v2 v7 v8 v5 v6 14
    RF0 v2 v3 v4 v1 v6 v7 v8 v5 15
    // 16-31
    LDR r0 , =0x7a879d8a
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 16
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 17
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 18
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 19
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 20
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 21
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 22
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 23
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 24
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 25
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 26
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 27
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 28
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 29
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 30
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 31
    // 32-47
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 32
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 33
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 34
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 35
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 36
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 37
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 38
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 39
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 40
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 41
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 42
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 43
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 44
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 45
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 46
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 47
    // 48-63
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 48
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 49
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 50
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 51
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 52
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 53
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 54
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 55
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 56
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 57
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 58
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 59
    RF1 v1 v2 v3 v4 v5 v6 v7 v8 60
    RF1 v4 v1 v2 v3 v8 v5 v6 v7 61
    RF1 v3 v4 v1 v2 v7 v8 v5 v6 62
    RF1 v2 v3 v4 v1 v6 v7 v8 v5 63
    // Load the state back and update it.
    ADD sp, sp, #16
    LDR ip, [sp]
    LDM ip!, {r0-r3}
    EOR v1, r0
    EOR v2, r1
    EOR v3, r2
    EOR v4, r3
    LDM ip!, {r0-r3}
    EOR v5, r0
    EOR v6, r1
    EOR v7, r2
    EOR v8, r3
    STMDB ip, {v1-v8}
    POP {r0-r2}
    ADD r1, r1, #0x40
	B .Lloop_start
	.Lloop_end:
    POP {v1-ip, lr}
    MOV pc, lr
.end
#endif