// Helper macros to help implement instructions defined as part of
// the RISC-V Zvknh[ab] extensions (vector SHA-256/SHA-512 cryptography).

#include "zvk_ext_macros.h"

#ifndef RISCV_ZVKNH_EXT_MACROS_H_
#define RISCV_ZVKNH_EXT_MACROS_H_

// Constraints common to all vsha* instructions, across all VSEW:
//  - VSEW is 32 (SHA-256) or 64 (SHA-512)
//  - No overlap of vd with vs1 or vs2.
//
// The constraint that vstart and vl are both EGS (4) aligned
// is checked in the VI_..._EGU32x4_..._LOOP and VI_..._EGU64x4_..._LOOP
// macros.
#define require_vsha2_common_constraints \
  do { \
    VI_CHECK_SSS(true) \
    require(P.VU.vsew == 32 || P.VU.vsew == 64); \
    require(insn.rd() != insn.rs1()); \
    require(insn.rd() != insn.rs2()); \
  } while (false)

// Constraints on vsha2 instructions that must be verified when VSEW==32.
// Those are *IN ADDITION* to the constraints checked by
// 'require_vsha2_common_constraints', which is meant to be run earlier.
//
// The constraint that vstart and vl are both EGS (4) aligned
// is checked in the VI_ZVK_..._EGU32x4_..._LOOP macros.
#define require_vsha2_vsew32_constraints \
  do { \
    require_zvknh_256; \
    require_egw_fits(128); \
  } while (false)

// Constraints on vsha2 instructions that must be verified when VSEW==32.
// Those are *IN ADDITION* to the constraints checked by
// 'require_vsha2_common_constraints', which is meant to be run earlier.
//
// The constraint that vstart and vl are both EGS (4) aligned
// is checked in the VI_ZVK_..._EGU64x4_..._LOOP macros.
#define require_vsha2_vsew64_constraints \
  do { \
    require_zvknh_512; \
    require_egw_fits(256); \
  } while (false)

//
// SHA-256 and SHA-512 common logic
//

// Ch(x, y, z) = (xy) ⊕ (~xz) = xy | ~xz
#define ZVK_SHA_CH(X, Y, Z) (((X) & (Y)) ^ ((~(X)) & (Z)))

// Maj(x,y,z)  = (xy) ⊕ (xz) ⊕(yz) = xy | xz | yz
#define ZVK_SHA_MAJ(X, Y, Z) (((X) & (Y)) ^ ((X) & (Z)) ^ ((Y) & (Z)))

//
// SHA-256
//

// sum0(x) = ROTR2(x) ⊕ ROTR13(x) ⊕ ROTR22(x)
#define ZVK_SHA256_SUM0(X) \
  (ZVK_ROR32(X, 2) ^ ZVK_ROR32(X, 13) ^ ZVK_ROR32(X, 22))

// sum1(x) = ROTR6(x) ⊕ ROTR11(x) ⊕ ROTR25(x)
#define ZVK_SHA256_SUM1(X) \
  (ZVK_ROR32(X, 6) ^ ZVK_ROR32(X, 11) ^ ZVK_ROR32(X, 25))

// sig0(x) = ROTR7(x) ⊕ ROTR18(x) ⊕ SHR3 (x)
#define ZVK_SHA256_SIG0(X) \
  (ZVK_ROR32(X, 7) ^ ZVK_ROR32(X, 18) ^ ((X) >> 3))

// sig1(x) = ROTR17(x) ⊕ ROTR19(x) ⊕ SHR10(x)
#define ZVK_SHA256_SIG1(X)  \
  (ZVK_ROR32(X, 17) ^ ZVK_ROR32(X, 19) ^ ((X) >> 10))

// Given the schedule words W[t+0], W[t+1], W[t+9], W[t+14], computes
// W[t+16].
#define ZVK_SHA256_SCHEDULE(W14, W9, W1, W0) \
    (ZVK_SHA256_SIG1(W14) + (W9) + ZVK_SHA256_SIG0(W1) + (W0))

// Performs one round of compression (out of the 64 rounds), given the state
// temporaries A,B,C,...,H, and KW, the sum Kt+Wt.
// Updates A,B,C,...,H to their new values. KW is not modified.
//
// Note that some of the logic could be omitted in vshac[ab] since
// some of the variables are dropped in each of those. However removing
// those unnecessary updates reduces the opportunities to share this single
// per-round logic and forces us to move further away from the how the logic
// is expressed in FIPS PUB 180-4.
#define ZVK_SHA256_COMPRESS(A, B, C, D, E, F, G, H, KW) \
  { \
    const uint32_t t1 = (H) + ZVK_SHA256_SUM1(E) + \
                        ZVK_SHA_CH((E), (F), (G)) + (KW); \
    const uint32_t t2 = ZVK_SHA256_SUM0(A) + ZVK_SHA_MAJ((A), (B), (C)); \
    (H) = (G); \
    (G) = (F); \
    (F) = (E); \
    (E) = (D) + t1; \
    (D) = (C); \
    (C) = (B); \
    (B) = (A); \
    (A) = t1 + t2; \
  }

//
// SHA-512
//

// sum0(x) = ROTR2(x) ⊕ ROTR13(x) ⊕ ROTR22(x)
#define ZVK_SHA512_SUM0(X) \
  (ZVK_ROR64(X, 28) ^ ZVK_ROR64(X, 34) ^ ZVK_ROR64(X, 39))

// sum1(x) = ROTR6(x) ⊕ ROTR11(x) ⊕ ROTR25(x)
#define ZVK_SHA512_SUM1(X) \
  (ZVK_ROR64(X, 14) ^ ZVK_ROR64(X, 18) ^ ZVK_ROR64(X, 41))

// sig0(x) = ROTR7(x) ⊕ ROTR18(x) ⊕ SHR3 (x)
#define ZVK_SHA512_SIG0(X) \
  (ZVK_ROR64(X, 1) ^ ZVK_ROR64(X, 8) ^ ((X) >> 7))

// sig1(x) = ROTR17(x) ⊕ ROTR19(x) ⊕ SHR10(x)
#define ZVK_SHA512_SIG1(X) \
  (ZVK_ROR64(X, 19) ^ ZVK_ROR64(X, 61) ^ ((X) >> 6))

// Given the schedule words W[t+0], W[t+1], W[t+9], W[t+14], computes
// W[t+16].
#define ZVK_SHA512_SCHEDULE(W14, W9, W1, W0) \
    (ZVK_SHA512_SIG1(W14) + (W9) + ZVK_SHA512_SIG0(W1) + (W0))

// Performs one round of compression (out of the 64 rounds), given the state
// temporaries A,B,C,...,H, and KW, the sum Kt+Wt.
// Updates A,B,C,...,H to their new values. KW is not modified.
//
// Note that some of the logic could be omitted in vshac[ab] since
// some of the variables are dropped in each of those. However removing
// those unnecessary updates reduces the opportunities to share this single
// per-round logic and forces us to move further away from the how the logic
// is expressed in FIPS PUB 180-4.
#define ZVK_SHA512_COMPRESS(A, B, C, D, E, F, G, H, KW) \
  { \
    const uint64_t t1 = (H) + ZVK_SHA512_SUM1(E) + \
                        ZVK_SHA_CH((E), (F), (G)) + (KW); \
    const uint64_t t2 = ZVK_SHA512_SUM0(A) + ZVK_SHA_MAJ((A), (B), (C)); \
    (H) = (G); \
    (G) = (F); \
    (F) = (E); \
    (E) = (D) + t1; \
    (D) = (C); \
    (C) = (B); \
    (B) = (A); \
    (A) = t1 + t2; \
  }

#endif  // RISCV_ZVKNH_EXT_MACROS_H_
