[[insns-vsm3c, SM3 Compression]]
= vsm3c.vi

Synopsis::
Vector SM3 Compression

Mnemonic::
vsm3c.vi vd, vs2, uimm

Encoding::
[wavedrom, , svg]
....
{reg:[
{bits: 7, name: 'OP-P'},
{bits: 5, name: 'vd'},
{bits: 3, name: 'OPMVV'},
{bits: 5, name: 'uimm'},
{bits: 5, name: 'vs2'},
{bits: 1, name: '1'},
{bits: 6, name: '101011'},
]}
....
Reserved Encodings::
* `SEW` is any value other than 32
* The `vd` register group overlaps with the `vs2` register group

Arguments::

[%autowidth]
[%header,cols="4,2,2,2,2,2"]
|===
|Register
|Direction
|EGW
|EGS
|EEW
|Definition

| Vd   | input  | 256  | 8 | 32 | Current state {H,G.F,E,D,C,B,A}
| uimm | input  | -    | - | -  | round number (rnds)
| Vs2  | input  | 256  | 8 | 32 | Message words {-,-,w[5],w[4],-,-,w[1],w[0]}
| Vd   | output | 256  | 8 | 32 | Next state {H,G.F,E,D,C,B,A}
|===

Description::
Two rounds of SM3 compression are performed.

The current state of eight 32-bit words is read in as an element group from `vd`. Eight 32-bit
message words are read in as an element group from `vs2`, although only four of them are used.
All of the 32-bit input words are byte-swapped from big endian to little endian.
These inputs are processed somewhat differently based on the round group (as specified in rnds),
and the next state is generated as an element group of eight 32-bit words.
The next state of eight 32-bit words are generated,
swapped from little endian to big endian, and are returned in
an eight-element group.

The round number is provided by the 5-bit `rnds` unsigned immediate. Legal values are 0 - 31
and indicate which group of two rounds are being performed. For example, if rnds=1,
then rounds 2 and 3 are being performed.

[NOTE]
====
The round number is used in the rotation of the constant as well to inform the
behavior which differs between rounds 0-15 and rounds 16-63.
====

[NOTE]
====
The endian byte swapping of the input and output words enables us to align with the SM3
specification without requiring that software perform these swaps.
====

[NOTE]
====
Preventing overlap between `vd` and `vs2` simplifies implementation with `VLEN < EGW`.
This restriction does not have any coding impact since proper implementation of the algorithm requires
that `vd` and `vs2` each are different registers.
====

// The elements are listed here in the order they appear in the register, with the most significant
// element on the left, and the least significant on the right.

// vs2 = {w[7], w[6], w[5], w[4], w[3], w[2], w[1], w[0]}

// The values consumed by the instruction are

// vs2 = {- , - , w[5], w[4], -, -, w[1], w[0]}

// Where the "-" characters are not consumed and are therefore don't cares.

// This instruction consumes the "W" message schedule inputs and internally generates the "W'" values as needed

// The number of element groups to be processed is `vl`/`EGS`.
// `vl` must be set to the number of `SEW=32` elements to be processed and
// therefore must be a multiple of `EGS=8`. +
// Likewise, `vstart` must be a multiple of `EGS=8`.

Operation::
[source,sail]
--
function clause execute (VSM3C(rnds, vs2, vd)) = {
  if(LMUL*VLEN < EGW)  then {
    handle_illegal();  // illegal instruction exception
    RETIRE_FAIL
  } else {

  eg_len = (vl/EGS)
  eg_start = (vstart/EGS)
  
  foreach (i from eg_start to eg_len-1) {

  // load state
  let {Hi @ Gi @ Fi @ Ei @ Di @ Ci @ Bi @ Ai} : bits(256) : bits(256) = (get_velem(vd, 256, i));
  //load message schedule
  let {u_w7 @ u_w6 @ w5i @ w4i @ u_w3 @ u_w2 @ w1i @ w0i} : bits(256) = (get_velem(vs2, 256, i));
  // u_w inputs are unused

// perform endian swap
let H : bits(32) = rev8(Hi);
let G : bits(32) = rev8(Gi);
let F : bits(32) = rev8(Fi);
let E : bits(32) = rev8(Ei);
let D : bits(32) = rev8(Di);
let C : bits(32) = rev8(Ci);
let B : bits(32) = rev8(Bi);
let A : bits(32) = rev8(Ai);

let w5 = : bits(32) rev8(w5i);
let w4 = : bits(32) rev8(w4i);
let w1 = : bits(32) rev8(w1i);
let w0 = : bits(32) rev8(w0i);

let x0 :bits(32) = w0 ^ w4;  // W'[0]
let x1 :bits(32) = w1 ^ w5;  // W'[1]

let j = 2 * rnds;
let ss1 : bits(32) = ROL32(ROL32(A, 12) + E + ROL32(T_j(j), j % 32), 7);
let ss2 : bits(32) = ss1 ^ ROL32(A, 12);
let tt1 : bits(32) = FF_j(A, B, C, j) + D + ss2 + x0;
let tt2 : bits(32) = GG_j(E, F, G, j) + H + ss1 + w0;
D = C;
let : bits(32) C1 = ROL32(B, 9);
B = A;
let A1 : bits(32) = tt1;
H = G;
let G1 : bits(32) = ROL32(F, 19);
F = E;
let E1 : bits(32) = P_0(tt2);

j = 2 * rnds + 1;
ss1 = ROL32(ROL32(A1, 12) + E1 + ROL32(T_j(j), j % 32), 7);
ss2 = ss1 ^ ROL32(A1, 12);
tt1 = FF_j(A1, B, C1, j) + D + ss2 + x1;
tt2 = GG_j(E1, F, G1, j) + H + ss1 + w1;
D = C1;
let C2 : bits(32) = ROL32(B, 9);
B = A1;
let A2 : bits(32) = tt1;
H = G1;
let G2 = : bits(32) ROL32(F, 19);
F = E1;
let E2 = : bits(32) P_0(tt2);

// Update the destination register - swap back to big endian
let result : bits(256) = {rev8(G1) @ rev8(G2) @ rev8(E1) @ rev8(E2) @ rev8(C1) @ rev8(C2) @ rev8(A1) @ rev8(A2)};
set_velem(vd, 256, i, result);
      }

RETIRE_SUCCESS
  }
}

function FF1(X, Y, Z) = ((X) ^ (Y) ^ (Z))
function FF2(X, Y, Z) = (((X) & (Y)) | ((X) & (Z)) | ((Y) & (Z)))

function FF_j(X, Y, Z, J) = (((J) <= 15) ? FF1(X, Y, Z) : FF2(X, Y, Z))

function GG1(X, Y, Z) = ((X) ^ (Y) ^ (Z))
function GG2(X, Y, Z) = (((X) & (Y)) | ((~(X)) & (Z)))
.
function GG_j(X, Y, Z, J) = (((J) <= 15) ? GG1(X, Y, Z) : GG2(X, Y, Z))

function T_j(J) = (((J) <= 15) ? (0x79CC4519) : (0x7A879D8A))

function P_0(X) = ((X) ^ ROL32((X),  9) ^ ROL32((X), 17))

--

Included in::
<<zvks>>, <<zvksc>>, <<zvksg>>, <<zvksh>>
