/*
* Copyright (c) 2019, Intel Corporation.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#include "precompiled.hpp"
#include "asm/assembler.hpp"
#include "asm/assembler.inline.hpp"
#include "runtime/stubRoutines.hpp"
#include "macroAssembler_x86.hpp"

#ifdef _LP64

void MacroAssembler::roundEnc(XMMRegister key, int rnum) {
    for (int xmm_reg_no = 0; xmm_reg_no <=rnum; xmm_reg_no++) {
      vaesenc(as_XMMRegister(xmm_reg_no), as_XMMRegister(xmm_reg_no), key, Assembler::AVX_512bit);
    }
}

void MacroAssembler::lastroundEnc(XMMRegister key, int rnum) {
    for (int xmm_reg_no = 0; xmm_reg_no <=rnum; xmm_reg_no++) {
      vaesenclast(as_XMMRegister(xmm_reg_no), as_XMMRegister(xmm_reg_no), key, Assembler::AVX_512bit);
    }
}

void MacroAssembler::roundDec(XMMRegister key, int rnum) {
    for (int xmm_reg_no = 0; xmm_reg_no <=rnum; xmm_reg_no++) {
      vaesdec(as_XMMRegister(xmm_reg_no), as_XMMRegister(xmm_reg_no), key, Assembler::AVX_512bit);
    }
}

void MacroAssembler::lastroundDec(XMMRegister key, int rnum) {
    for (int xmm_reg_no = 0; xmm_reg_no <=rnum; xmm_reg_no++) {
      vaesdeclast(as_XMMRegister(xmm_reg_no), as_XMMRegister(xmm_reg_no), key, Assembler::AVX_512bit);
    }
}

// Load key and shuffle operation
void MacroAssembler::ev_load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) {
    movdqu(xmmdst, Address(key, offset));
    if (xmm_shuf_mask != NULL) {
        pshufb(xmmdst, xmm_shuf_mask);
    } else {
       pshufb(xmmdst, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr()));
    }
   evshufi64x2(xmmdst, xmmdst, xmmdst, 0x0, Assembler::AVX_512bit);
}

// AES-ECB Encrypt Operation
void MacroAssembler::aesecb_encrypt(Register src_addr, Register dest_addr, Register key, Register len) {

    const Register pos = rax;
    const Register rounds = r12;

    Label NO_PARTS, LOOP, Loop_start, LOOP2, AES192, END_LOOP, AES256, REMAINDER, LAST2, END, KEY_192, KEY_256, EXIT;
    push(r13);
    push(r12);

    // For EVEX with VL and BW, provide a standard mask, VL = 128 will guide the merge
    // context for the registers used, where all instructions below are using 128-bit mode
    // On EVEX without VL and BW, these instructions will all be AVX.
    if (VM_Version::supports_avx512vlbw()) {
       movl(rax, 0xffff);
       kmovql(k1, rax);
    }
    push(len); // Save
    push(rbx);

    vzeroupper();

    xorptr(pos, pos);

    // Calculate number of rounds based on key length(128, 192, 256):44 for 10-rounds, 52 for 12-rounds, 60 for 14-rounds
    movl(rounds, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)));

    // Load Key shuf mask
    const XMMRegister xmm_key_shuf_mask = xmm31;  // used temporarily to swap key bytes up front
    movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr()));

    // Load and shuffle key based on number of rounds
    ev_load_key(xmm8, key, 0 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm9, key, 1 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm10, key, 2 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm23, key, 3 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm12, key, 4 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm13, key, 5 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm14, key, 6 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm15, key, 7 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm16, key, 8 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm17, key, 9 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm24, key, 10 * 16, xmm_key_shuf_mask);
    cmpl(rounds, 52);
    jcc(Assembler::greaterEqual, KEY_192);
    jmp(Loop_start);

    bind(KEY_192);
    ev_load_key(xmm19, key, 11 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm20, key, 12 * 16, xmm_key_shuf_mask);
    cmpl(rounds, 60);
    jcc(Assembler::equal, KEY_256);
    jmp(Loop_start);

    bind(KEY_256);
    ev_load_key(xmm21, key, 13 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm22, key, 14 * 16, xmm_key_shuf_mask);

    bind(Loop_start);
    movq(rbx, len);
    // Divide length by 16 to convert it to number of blocks
    shrq(len, 4);
    shlq(rbx, 60);
    jcc(Assembler::equal, NO_PARTS);
    addq(len, 1);
    // Check if number of blocks is greater than or equal to 32
    // If true, 512 bytes are processed at a time (code marked by label LOOP)
    // If not, 16 bytes are processed (code marked by REMAINDER label)
    bind(NO_PARTS);
    movq(rbx, len);
    shrq(len, 5);
    jcc(Assembler::equal, REMAINDER);
    movl(r13, len);
    // Compute number of blocks that will be processed 512 bytes at a time
    // Subtract this from the total number of blocks which will then be processed by REMAINDER loop
    shlq(r13, 5);
    subq(rbx, r13);
    //Begin processing 512 bytes
    bind(LOOP);
    // Move 64 bytes of PT data into a zmm register, as a result 512 bytes of PT loaded in zmm0-7
    evmovdquq(xmm0, Address(src_addr, pos, Address::times_1, 0 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm1, Address(src_addr, pos, Address::times_1, 1 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm2, Address(src_addr, pos, Address::times_1, 2 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm3, Address(src_addr, pos, Address::times_1, 3 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm4, Address(src_addr, pos, Address::times_1, 4 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm5, Address(src_addr, pos, Address::times_1, 5 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm6, Address(src_addr, pos, Address::times_1, 6 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm7, Address(src_addr, pos, Address::times_1, 7 * 64), Assembler::AVX_512bit);
    // Xor with the first round key
    evpxorq(xmm0, xmm0, xmm8, Assembler::AVX_512bit);
    evpxorq(xmm1, xmm1, xmm8, Assembler::AVX_512bit);
    evpxorq(xmm2, xmm2, xmm8, Assembler::AVX_512bit);
    evpxorq(xmm3, xmm3, xmm8, Assembler::AVX_512bit);
    evpxorq(xmm4, xmm4, xmm8, Assembler::AVX_512bit);
    evpxorq(xmm5, xmm5, xmm8, Assembler::AVX_512bit);
    evpxorq(xmm6, xmm6, xmm8, Assembler::AVX_512bit);
    evpxorq(xmm7, xmm7, xmm8, Assembler::AVX_512bit);
    // 9 Aes encode round operations
    roundEnc(xmm9,  7);
    roundEnc(xmm10, 7);
    roundEnc(xmm23, 7);
    roundEnc(xmm12, 7);
    roundEnc(xmm13, 7);
    roundEnc(xmm14, 7);
    roundEnc(xmm15, 7);
    roundEnc(xmm16, 7);
    roundEnc(xmm17, 7);
    cmpl(rounds, 52);
    jcc(Assembler::aboveEqual, AES192);
    // Aesenclast round operation for keysize = 128
    lastroundEnc(xmm24, 7);
    jmp(END_LOOP);
    //Additional 2 rounds of Aesenc operation for keysize = 192
    bind(AES192);
    roundEnc(xmm24, 7);
    roundEnc(xmm19, 7);
    cmpl(rounds, 60);
    jcc(Assembler::aboveEqual, AES256);
    // Aesenclast round for keysize = 192
    lastroundEnc(xmm20, 7);
    jmp(END_LOOP);
    // 2 rounds of Aesenc operation and Aesenclast for keysize = 256
    bind(AES256);
    roundEnc(xmm20, 7);
    roundEnc(xmm21, 7);
    lastroundEnc(xmm22, 7);

    bind(END_LOOP);
    // Move 512 bytes of CT to destination
    evmovdquq(Address(dest_addr, pos, Address::times_1, 0 * 64), xmm0, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 1 * 64), xmm1, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 2 * 64), xmm2, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 3 * 64), xmm3, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 4 * 64), xmm4, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 5 * 64), xmm5, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 6 * 64), xmm6, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 7 * 64), xmm7, Assembler::AVX_512bit);

    addq(pos, 512);
    decq(len);
    jcc(Assembler::notEqual, LOOP);

    bind(REMAINDER);
    vzeroupper();
    cmpq(rbx, 0);
    jcc(Assembler::equal, END);
    // Process 16 bytes at a time
    bind(LOOP2);
    movdqu(xmm1, Address(src_addr, pos, Address::times_1, 0));
    vpxor(xmm1, xmm1, xmm8, Assembler::AVX_128bit);
    // xmm2 contains shuffled key for Aesenclast operation.
    vmovdqu(xmm2, xmm24);

    vaesenc(xmm1, xmm1, xmm9, Assembler::AVX_128bit);
    vaesenc(xmm1, xmm1, xmm10, Assembler::AVX_128bit);
    vaesenc(xmm1, xmm1, xmm23, Assembler::AVX_128bit);
    vaesenc(xmm1, xmm1, xmm12, Assembler::AVX_128bit);
    vaesenc(xmm1, xmm1, xmm13, Assembler::AVX_128bit);
    vaesenc(xmm1, xmm1, xmm14, Assembler::AVX_128bit);
    vaesenc(xmm1, xmm1, xmm15, Assembler::AVX_128bit);
    vaesenc(xmm1, xmm1, xmm16, Assembler::AVX_128bit);
    vaesenc(xmm1, xmm1, xmm17, Assembler::AVX_128bit);

    cmpl(rounds, 52);
    jcc(Assembler::below, LAST2);
    vmovdqu(xmm2, xmm20);
    vaesenc(xmm1, xmm1, xmm24, Assembler::AVX_128bit);
    vaesenc(xmm1, xmm1, xmm19, Assembler::AVX_128bit);
    cmpl(rounds, 60);
    jcc(Assembler::below, LAST2);
    vmovdqu(xmm2, xmm22);
    vaesenc(xmm1, xmm1, xmm20, Assembler::AVX_128bit);
    vaesenc(xmm1, xmm1, xmm21, Assembler::AVX_128bit);

    bind(LAST2);
    // Aesenclast round
    vaesenclast(xmm1, xmm1, xmm2, Assembler::AVX_128bit);
    // Write 16 bytes of CT to destination
    movdqu(Address(dest_addr, pos, Address::times_1, 0), xmm1);
    addq(pos, 16);
    decq(rbx);
    jcc(Assembler::notEqual, LOOP2);

    bind(END);
    // Zero out the round keys
    evpxorq(xmm8, xmm8, xmm8, Assembler::AVX_512bit);
    evpxorq(xmm9, xmm9, xmm9, Assembler::AVX_512bit);
    evpxorq(xmm10, xmm10, xmm10, Assembler::AVX_512bit);
    evpxorq(xmm23, xmm23, xmm23, Assembler::AVX_512bit);
    evpxorq(xmm12, xmm12, xmm12, Assembler::AVX_512bit);
    evpxorq(xmm13, xmm13, xmm13, Assembler::AVX_512bit);
    evpxorq(xmm14, xmm14, xmm14, Assembler::AVX_512bit);
    evpxorq(xmm15, xmm15, xmm15, Assembler::AVX_512bit);
    evpxorq(xmm16, xmm16, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm17, xmm17, xmm17, Assembler::AVX_512bit);
    evpxorq(xmm24, xmm24, xmm24, Assembler::AVX_512bit);
    cmpl(rounds, 44);
    jcc(Assembler::belowEqual, EXIT);
    evpxorq(xmm19, xmm19, xmm19, Assembler::AVX_512bit);
    evpxorq(xmm20, xmm20, xmm20, Assembler::AVX_512bit);
    cmpl(rounds, 52);
    jcc(Assembler::belowEqual, EXIT);
    evpxorq(xmm21, xmm21, xmm21, Assembler::AVX_512bit);
    evpxorq(xmm22, xmm22, xmm22, Assembler::AVX_512bit);
    bind(EXIT);
    pop(rbx);
    pop(rax); // return length
    pop(r12);
    pop(r13);
}

// AES-ECB Decrypt Operation
void MacroAssembler::aesecb_decrypt(Register src_addr, Register dest_addr, Register key, Register len)  {

    Label NO_PARTS, LOOP, Loop_start, LOOP2, AES192, END_LOOP, AES256, REMAINDER, LAST2, END, KEY_192, KEY_256, EXIT;
    const Register pos = rax;
    const Register rounds = r12;
    push(r13);
    push(r12);

    // For EVEX with VL and BW, provide a standard mask, VL = 128 will guide the merge
    // context for the registers used, where all instructions below are using 128-bit mode
    // On EVEX without VL and BW, these instructions will all be AVX.
    if (VM_Version::supports_avx512vlbw()) {
       movl(rax, 0xffff);
       kmovql(k1, rax);
    }

    push(len); // Save
    push(rbx);

    vzeroupper();

    xorptr(pos, pos);
    // Calculate number of rounds i.e. based on key length(128, 192, 256):44 for 10-rounds, 52 for 12-rounds, 60 for 14-rounds
    movl(rounds, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)));

    // Load Key shuf mask
    const XMMRegister xmm_key_shuf_mask = xmm31;  // used temporarily to swap key bytes up front
    movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr()));

    // Load and shuffle round keys. The java expanded key ordering is rotated one position in decryption.
    // So the first round key is loaded from 1*16 here and last round key is loaded from 0*16
    ev_load_key(xmm9,  key, 1 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm10, key, 2 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm11, key, 3 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm12, key, 4 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm13, key, 5 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm14, key, 6 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm15, key, 7 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm16, key, 8 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm17, key, 9 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm18, key, 10 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm27, key, 0 * 16, xmm_key_shuf_mask);
    cmpl(rounds, 52);
    jcc(Assembler::greaterEqual, KEY_192);
    jmp(Loop_start);

    bind(KEY_192);
    ev_load_key(xmm19, key, 11 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm20, key, 12 * 16, xmm_key_shuf_mask);
    cmpl(rounds, 60);
    jcc(Assembler::equal, KEY_256);
    jmp(Loop_start);

    bind(KEY_256);
    ev_load_key(xmm21, key, 13 * 16, xmm_key_shuf_mask);
    ev_load_key(xmm22, key, 14 * 16, xmm_key_shuf_mask);
    bind(Loop_start);
    movq(rbx, len);
    // Convert input length to number of blocks
    shrq(len, 4);
    shlq(rbx, 60);
    jcc(Assembler::equal, NO_PARTS);
    addq(len, 1);
    // Check if number of blocks is greater than/ equal to 32
    // If true, blocks then 512 bytes are processed at a time (code marked by label LOOP)
    // If not, 16 bytes are processed (code marked by label REMAINDER)
    bind(NO_PARTS);
    movq(rbx, len);
    shrq(len, 5);
    jcc(Assembler::equal, REMAINDER);
    movl(r13, len);
    // Compute number of blocks that will be processed as 512 bytes at a time
    // Subtract this from the total number of blocks, which will then be processed by REMAINDER loop.
    shlq(r13, 5);
    subq(rbx, r13);

    bind(LOOP);
    // Move 64 bytes of CT data into a zmm register, as a result 512 bytes of CT loaded in zmm0-7
    evmovdquq(xmm0, Address(src_addr, pos, Address::times_1, 0 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm1, Address(src_addr, pos, Address::times_1, 1 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm2, Address(src_addr, pos, Address::times_1, 2 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm3, Address(src_addr, pos, Address::times_1, 3 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm4, Address(src_addr, pos, Address::times_1, 4 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm5, Address(src_addr, pos, Address::times_1, 5 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm6, Address(src_addr, pos, Address::times_1, 6 * 64), Assembler::AVX_512bit);
    evmovdquq(xmm7, Address(src_addr, pos, Address::times_1, 7 * 64), Assembler::AVX_512bit);
    // Xor with the first round key
    evpxorq(xmm0, xmm0, xmm9, Assembler::AVX_512bit);
    evpxorq(xmm1, xmm1, xmm9, Assembler::AVX_512bit);
    evpxorq(xmm2, xmm2, xmm9, Assembler::AVX_512bit);
    evpxorq(xmm3, xmm3, xmm9, Assembler::AVX_512bit);
    evpxorq(xmm4, xmm4, xmm9, Assembler::AVX_512bit);
    evpxorq(xmm5, xmm5, xmm9, Assembler::AVX_512bit);
    evpxorq(xmm6, xmm6, xmm9, Assembler::AVX_512bit);
    evpxorq(xmm7, xmm7, xmm9, Assembler::AVX_512bit);
    // 9 rounds of Aesdec
    roundDec(xmm10, 7);
    roundDec(xmm11, 7);
    roundDec(xmm12, 7);
    roundDec(xmm13, 7);
    roundDec(xmm14, 7);
    roundDec(xmm15, 7);
    roundDec(xmm16, 7);
    roundDec(xmm17, 7);
    roundDec(xmm18, 7);
    cmpl(rounds, 52);
    jcc(Assembler::aboveEqual, AES192);
    // Aesdeclast round for keysize = 128
    lastroundDec(xmm27, 7);
    jmp(END_LOOP);

    bind(AES192);
    // 2 Additional rounds for keysize = 192
    roundDec(xmm19, 7);
    roundDec(xmm20, 7);
    cmpl(rounds, 60);
    jcc(Assembler::aboveEqual, AES256);
    // Aesdeclast round for keysize = 192
    lastroundDec(xmm27, 7);
    jmp(END_LOOP);
    bind(AES256);
    // 2 Additional rounds and Aesdeclast for keysize = 256
    roundDec(xmm21, 7);
    roundDec(xmm22, 7);
    lastroundDec(xmm27, 7);

    bind(END_LOOP);
    // Write 512 bytes of PT to the destination
    evmovdquq(Address(dest_addr, pos, Address::times_1, 0 * 64), xmm0, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 1 * 64), xmm1, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 2 * 64), xmm2, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 3 * 64), xmm3, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 4 * 64), xmm4, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 5 * 64), xmm5, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 6 * 64), xmm6, Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 7 * 64), xmm7, Assembler::AVX_512bit);

    addq(pos, 512);
    decq(len);
    jcc(Assembler::notEqual, LOOP);

    bind(REMAINDER);
    vzeroupper();
    cmpq(rbx, 0);
    jcc(Assembler::equal, END);
    // Process 16 bytes at a time
    bind(LOOP2);
    movdqu(xmm1, Address(src_addr, pos, Address::times_1, 0));
    vpxor(xmm1, xmm1, xmm9, Assembler::AVX_128bit);
    // xmm2 contains shuffled key for Aesdeclast operation.
    vmovdqu(xmm2, xmm27);

    vaesdec(xmm1, xmm1, xmm10, Assembler::AVX_128bit);
    vaesdec(xmm1, xmm1, xmm11, Assembler::AVX_128bit);
    vaesdec(xmm1, xmm1, xmm12, Assembler::AVX_128bit);
    vaesdec(xmm1, xmm1, xmm13, Assembler::AVX_128bit);
    vaesdec(xmm1, xmm1, xmm14, Assembler::AVX_128bit);
    vaesdec(xmm1, xmm1, xmm15, Assembler::AVX_128bit);
    vaesdec(xmm1, xmm1, xmm16, Assembler::AVX_128bit);
    vaesdec(xmm1, xmm1, xmm17, Assembler::AVX_128bit);
    vaesdec(xmm1, xmm1, xmm18, Assembler::AVX_128bit);

    cmpl(rounds, 52);
    jcc(Assembler::below, LAST2);
    vaesdec(xmm1, xmm1, xmm19, Assembler::AVX_128bit);
    vaesdec(xmm1, xmm1, xmm20, Assembler::AVX_128bit);
    cmpl(rounds, 60);
    jcc(Assembler::below, LAST2);
    vaesdec(xmm1, xmm1, xmm21, Assembler::AVX_128bit);
    vaesdec(xmm1, xmm1, xmm22, Assembler::AVX_128bit);

    bind(LAST2);
    // Aesdeclast round
    vaesdeclast(xmm1, xmm1, xmm2, Assembler::AVX_128bit);
    // Write 16 bytes of PT to destination
    movdqu(Address(dest_addr, pos, Address::times_1, 0), xmm1);
    addq(pos, 16);
    decq(rbx);
    jcc(Assembler::notEqual, LOOP2);

    bind(END);
    // Zero out the round keys
    evpxorq(xmm8, xmm8, xmm8, Assembler::AVX_512bit);
    evpxorq(xmm9, xmm9, xmm9, Assembler::AVX_512bit);
    evpxorq(xmm10, xmm10, xmm10, Assembler::AVX_512bit);
    evpxorq(xmm11, xmm11, xmm11, Assembler::AVX_512bit);
    evpxorq(xmm12, xmm12, xmm12, Assembler::AVX_512bit);
    evpxorq(xmm13, xmm13, xmm13, Assembler::AVX_512bit);
    evpxorq(xmm14, xmm14, xmm14, Assembler::AVX_512bit);
    evpxorq(xmm15, xmm15, xmm15, Assembler::AVX_512bit);
    evpxorq(xmm16, xmm16, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm17, xmm17, xmm17, Assembler::AVX_512bit);
    evpxorq(xmm18, xmm18, xmm18, Assembler::AVX_512bit);
    evpxorq(xmm27, xmm27, xmm27, Assembler::AVX_512bit);
    cmpl(rounds, 44);
    jcc(Assembler::belowEqual, EXIT);
    evpxorq(xmm19, xmm19, xmm19, Assembler::AVX_512bit);
    evpxorq(xmm20, xmm20, xmm20, Assembler::AVX_512bit);
    cmpl(rounds, 52);
    jcc(Assembler::belowEqual, EXIT);
    evpxorq(xmm21, xmm21, xmm21, Assembler::AVX_512bit);
    evpxorq(xmm22, xmm22, xmm22, Assembler::AVX_512bit);
    bind(EXIT);
    pop(rbx);
    pop(rax); // return length
    pop(r12);
    pop(r13);
}

// Multiply 128 x 128 bits, using 4 pclmulqdq operations
void MacroAssembler::schoolbookAAD(int i, Register htbl, XMMRegister data,
    XMMRegister tmp0, XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3) {
    movdqu(xmm15, Address(htbl, i * 16));
    vpclmulhqlqdq(tmp3, data, xmm15); // 0x01
    vpxor(tmp2, tmp2, tmp3, Assembler::AVX_128bit);
    vpclmulldq(tmp3, data, xmm15); // 0x00
    vpxor(tmp0, tmp0, tmp3, Assembler::AVX_128bit);
    vpclmulhdq(tmp3, data, xmm15); // 0x11
    vpxor(tmp1, tmp1, tmp3, Assembler::AVX_128bit);
    vpclmullqhqdq(tmp3, data, xmm15); // 0x10
    vpxor(tmp2, tmp2, tmp3, Assembler::AVX_128bit);
}

// Multiply two 128 bit numbers resulting in a 256 bit value
// Result of the multiplication followed by reduction stored in state
void MacroAssembler::gfmul(XMMRegister tmp0, XMMRegister state) {
    const XMMRegister tmp1 = xmm4;
    const XMMRegister tmp2 = xmm5;
    const XMMRegister tmp3 = xmm6;
    const XMMRegister tmp4 = xmm7;

    vpclmulldq(tmp1, state, tmp0); //0x00  (a0 * b0)
    vpclmulhdq(tmp4, state, tmp0);//0x11 (a1 * b1)
    vpclmullqhqdq(tmp2, state, tmp0);//0x10 (a1 * b0)
    vpclmulhqlqdq(tmp3, state, tmp0); //0x01 (a0 * b1)

    vpxor(tmp2, tmp2, tmp3, Assembler::AVX_128bit); // (a0 * b1) + (a1 * b0)

    vpslldq(tmp3, tmp2, 8, Assembler::AVX_128bit);
    vpsrldq(tmp2, tmp2, 8, Assembler::AVX_128bit);
    vpxor(tmp1, tmp1, tmp3, Assembler::AVX_128bit); // tmp1 and tmp4 hold the result
    vpxor(tmp4, tmp4, tmp2, Assembler::AVX_128bit); // of carryless multiplication
    // Follows the reduction technique mentioned in
    // Shift-XOR reduction described in Gueron-Kounavis May 2010
    // First phase of reduction
    //
    vpslld(xmm8, tmp1, 31, Assembler::AVX_128bit); // packed right shift shifting << 31
    vpslld(xmm9, tmp1, 30, Assembler::AVX_128bit); // packed right shift shifting << 30
    vpslld(xmm10, tmp1, 25, Assembler::AVX_128bit);// packed right shift shifting << 25
    // xor the shifted versions
    vpxor(xmm8, xmm8, xmm9, Assembler::AVX_128bit);
    vpxor(xmm8, xmm8, xmm10, Assembler::AVX_128bit);
    vpslldq(xmm9, xmm8, 12, Assembler::AVX_128bit);
    vpsrldq(xmm8, xmm8, 4, Assembler::AVX_128bit);
    vpxor(tmp1, tmp1, xmm9, Assembler::AVX_128bit);// first phase of the reduction complete
    //
    // Second phase of the reduction
    //
    vpsrld(xmm9, tmp1, 1, Assembler::AVX_128bit);// packed left shifting >> 1
    vpsrld(xmm10, tmp1, 2, Assembler::AVX_128bit);// packed left shifting >> 2
    vpsrld(xmm11, tmp1, 7, Assembler::AVX_128bit);// packed left shifting >> 7
    vpxor(xmm9, xmm9, xmm10, Assembler::AVX_128bit);// xor the shifted versions
    vpxor(xmm9, xmm9, xmm11, Assembler::AVX_128bit);
    vpxor(xmm9, xmm9, xmm8, Assembler::AVX_128bit);
    vpxor(tmp1, tmp1, xmm9, Assembler::AVX_128bit);
    vpxor(state, tmp4, tmp1, Assembler::AVX_128bit);// the result is in state
    ret(0);
}

// This method takes the subkey after expansion as input and generates 1 * 16 power of subkey H.
// The power of H is used in reduction process for one block ghash
void MacroAssembler::generateHtbl_one_block(Register htbl) {
    const XMMRegister t = xmm13;

    // load the original subkey hash
    movdqu(t, Address(htbl, 0));
    // shuffle using long swap mask
    movdqu(xmm10, ExternalAddress(StubRoutines::x86::ghash_long_swap_mask_addr()));
    vpshufb(t, t, xmm10, Assembler::AVX_128bit);

    // Compute H' = GFMUL(H, 2)
    vpsrld(xmm3, t, 7, Assembler::AVX_128bit);
    movdqu(xmm4, ExternalAddress(StubRoutines::x86::ghash_shufflemask_addr()));
    vpshufb(xmm3, xmm3, xmm4, Assembler::AVX_128bit);
    movl(rax, 0xff00);
    movdl(xmm4, rax);
    vpshufb(xmm4, xmm4, xmm3, Assembler::AVX_128bit);
    movdqu(xmm5, ExternalAddress(StubRoutines::x86::ghash_polynomial_addr()));
    vpand(xmm5, xmm5, xmm4, Assembler::AVX_128bit);
    vpsrld(xmm3, t, 31, Assembler::AVX_128bit);
    vpslld(xmm4, t, 1, Assembler::AVX_128bit);
    vpslldq(xmm3, xmm3, 4, Assembler::AVX_128bit);
    vpxor(t, xmm4, xmm3, Assembler::AVX_128bit);// t holds p(x) <<1 or H * 2

    //Adding p(x)<<1 to xmm5 which holds the reduction polynomial
    vpxor(t, t, xmm5, Assembler::AVX_128bit);
    movdqu(Address(htbl, 1 * 16), t); // H * 2

    ret(0);
}

// This method takes the subkey after expansion as input and generates the remaining powers of subkey H.
// The power of H is used in reduction process for eight block ghash
void MacroAssembler::generateHtbl_eight_blocks(Register htbl) {
    const XMMRegister t = xmm13;
    const XMMRegister tmp0 = xmm1;
    Label GFMUL;

    movdqu(t, Address(htbl, 1 * 16));
    movdqu(tmp0, t);

    // tmp0 and t hold H. Now we compute powers of H by using GFMUL(H, H)
    call(GFMUL, relocInfo::none);
    movdqu(Address(htbl, 2 * 16), t); //H ^ 2 * 2
    call(GFMUL, relocInfo::none);
    movdqu(Address(htbl, 3 * 16), t); //H ^ 3 * 2
    call(GFMUL, relocInfo::none);
    movdqu(Address(htbl, 4 * 16), t); //H ^ 4 * 2
    call(GFMUL, relocInfo::none);
    movdqu(Address(htbl, 5 * 16), t); //H ^ 5 * 2
    call(GFMUL, relocInfo::none);
    movdqu(Address(htbl, 6 * 16), t); //H ^ 6 * 2
    call(GFMUL, relocInfo::none);
    movdqu(Address(htbl, 7 * 16), t); //H ^ 7 * 2
    call(GFMUL, relocInfo::none);
    movdqu(Address(htbl, 8 * 16), t); //H ^ 8 * 2
    ret(0);

    bind(GFMUL);
    gfmul(tmp0, t);
}

// Multiblock and single block GHASH computation using Shift XOR reduction technique
void MacroAssembler::avx_ghash(Register input_state, Register htbl,
    Register input_data, Register blocks) {

    // temporary variables to hold input data and input state
    const XMMRegister data = xmm1;
    const XMMRegister state = xmm0;
    // temporary variables to hold intermediate results
    const XMMRegister tmp0 = xmm3;
    const XMMRegister tmp1 = xmm4;
    const XMMRegister tmp2 = xmm5;
    const XMMRegister tmp3 = xmm6;
    // temporary variables to hold byte and long swap masks
    const XMMRegister bswap_mask = xmm2;
    const XMMRegister lswap_mask = xmm14;

    Label GENERATE_HTBL_1_BLK, GENERATE_HTBL_8_BLKS, BEGIN_PROCESS, GFMUL, BLOCK8_REDUCTION,
          ONE_BLK_INIT, PROCESS_1_BLOCK, PROCESS_8_BLOCKS, SAVE_STATE, EXIT_GHASH;

    testptr(blocks, blocks);
    jcc(Assembler::zero, EXIT_GHASH);

    // Check if Hashtable (1*16) has been already generated
    // For anything less than 8 blocks, we generate only the first power of H.
    movdqu(tmp2, Address(htbl, 1 * 16));
    ptest(tmp2, tmp2);
    jcc(Assembler::notZero, BEGIN_PROCESS);
    call(GENERATE_HTBL_1_BLK, relocInfo::none);

    // Shuffle the input state
    bind(BEGIN_PROCESS);
    movdqu(lswap_mask, ExternalAddress(StubRoutines::x86::ghash_long_swap_mask_addr()));
    movdqu(state, Address(input_state, 0));
    vpshufb(state, state, lswap_mask, Assembler::AVX_128bit);

    cmpl(blocks, 8);
    jcc(Assembler::below, ONE_BLK_INIT);
    // If we have 8 blocks or more data, then generate remaining powers of H
    movdqu(tmp2, Address(htbl, 8 * 16));
    ptest(tmp2, tmp2);
    jcc(Assembler::notZero, PROCESS_8_BLOCKS);
    call(GENERATE_HTBL_8_BLKS, relocInfo::none);

    //Do 8 multiplies followed by a reduction processing 8 blocks of data at a time
    //Each block = 16 bytes.
    bind(PROCESS_8_BLOCKS);
    subl(blocks, 8);
    movdqu(bswap_mask, ExternalAddress(StubRoutines::x86::ghash_byte_swap_mask_addr()));
    movdqu(data, Address(input_data, 16 * 7));
    vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
    //Loading 1*16 as calculated powers of H required starts at that location.
    movdqu(xmm15, Address(htbl, 1 * 16));
    //Perform carryless multiplication of (H*2, data block #7)
    vpclmulhqlqdq(tmp2, data, xmm15);//a0 * b1
    vpclmulldq(tmp0, data, xmm15);//a0 * b0
    vpclmulhdq(tmp1, data, xmm15);//a1 * b1
    vpclmullqhqdq(tmp3, data, xmm15);//a1* b0
    vpxor(tmp2, tmp2, tmp3, Assembler::AVX_128bit);// (a0 * b1) + (a1 * b0)

    movdqu(data, Address(input_data, 16 * 6));
    vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
    // Perform carryless multiplication of (H^2 * 2, data block #6)
    schoolbookAAD(2, htbl, data, tmp0, tmp1, tmp2, tmp3);

    movdqu(data, Address(input_data, 16 * 5));
    vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
    // Perform carryless multiplication of (H^3 * 2, data block #5)
    schoolbookAAD(3, htbl, data, tmp0, tmp1, tmp2, tmp3);
    movdqu(data, Address(input_data, 16 * 4));
    vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
    // Perform carryless multiplication of (H^4 * 2, data block #4)
    schoolbookAAD(4, htbl, data, tmp0, tmp1, tmp2, tmp3);
    movdqu(data, Address(input_data, 16 * 3));
    vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
    // Perform carryless multiplication of (H^5 * 2, data block #3)
    schoolbookAAD(5, htbl, data, tmp0, tmp1, tmp2, tmp3);
    movdqu(data, Address(input_data, 16 * 2));
    vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
    // Perform carryless multiplication of (H^6 * 2, data block #2)
    schoolbookAAD(6, htbl, data, tmp0, tmp1, tmp2, tmp3);
    movdqu(data, Address(input_data, 16 * 1));
    vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
    // Perform carryless multiplication of (H^7 * 2, data block #1)
    schoolbookAAD(7, htbl, data, tmp0, tmp1, tmp2, tmp3);
    movdqu(data, Address(input_data, 16 * 0));
    // xor data block#0 with input state before perfoming carry-less multiplication
    vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
    vpxor(data, data, state, Assembler::AVX_128bit);
    // Perform carryless multiplication of (H^8 * 2, data block #0)
    schoolbookAAD(8, htbl, data, tmp0, tmp1, tmp2, tmp3);
    vpslldq(tmp3, tmp2, 8, Assembler::AVX_128bit);
    vpsrldq(tmp2, tmp2, 8, Assembler::AVX_128bit);
    vpxor(tmp0, tmp0, tmp3, Assembler::AVX_128bit);// tmp0, tmp1 contains aggregated results of
    vpxor(tmp1, tmp1, tmp2, Assembler::AVX_128bit);// the multiplication operation

    // we have the 2 128-bit partially accumulated multiplication results in tmp0:tmp1
    // with higher 128-bit in tmp1 and lower 128-bit in corresponding tmp0
    // Follows the reduction technique mentioned in
    // Shift-XOR reduction described in Gueron-Kounavis May 2010
    bind(BLOCK8_REDUCTION);
    // First Phase of the reduction
    vpslld(xmm8, tmp0, 31, Assembler::AVX_128bit); // packed right shifting << 31
    vpslld(xmm9, tmp0, 30, Assembler::AVX_128bit); // packed right shifting << 30
    vpslld(xmm10, tmp0, 25, Assembler::AVX_128bit); // packed right shifting << 25
    // xor the shifted versions
    vpxor(xmm8, xmm8, xmm10, Assembler::AVX_128bit);
    vpxor(xmm8, xmm8, xmm9, Assembler::AVX_128bit);

    vpslldq(xmm9, xmm8, 12, Assembler::AVX_128bit);
    vpsrldq(xmm8, xmm8, 4, Assembler::AVX_128bit);

    vpxor(tmp0, tmp0, xmm9, Assembler::AVX_128bit); // first phase of reduction is complete
    // second phase of the reduction
    vpsrld(xmm9, tmp0, 1, Assembler::AVX_128bit); // packed left shifting >> 1
    vpsrld(xmm10, tmp0, 2, Assembler::AVX_128bit); // packed left shifting >> 2
    vpsrld(tmp2, tmp0, 7, Assembler::AVX_128bit); // packed left shifting >> 7
    // xor the shifted versions
    vpxor(xmm9, xmm9, xmm10, Assembler::AVX_128bit);
    vpxor(xmm9, xmm9, tmp2, Assembler::AVX_128bit);
    vpxor(xmm9, xmm9, xmm8, Assembler::AVX_128bit);
    vpxor(tmp0, xmm9, tmp0, Assembler::AVX_128bit);
    // Final result is in state
    vpxor(state, tmp0, tmp1, Assembler::AVX_128bit);

    lea(input_data, Address(input_data, 16 * 8));
    cmpl(blocks, 8);
    jcc(Assembler::below, ONE_BLK_INIT);
    jmp(PROCESS_8_BLOCKS);

    // Since this is one block operation we will only use H * 2 i.e. the first power of H
    bind(ONE_BLK_INIT);
    movdqu(tmp0, Address(htbl, 1 * 16));
    movdqu(bswap_mask, ExternalAddress(StubRoutines::x86::ghash_byte_swap_mask_addr()));

    //Do one (128 bit x 128 bit) carry-less multiplication at a time followed by a reduction.
    bind(PROCESS_1_BLOCK);
    cmpl(blocks, 0);
    jcc(Assembler::equal, SAVE_STATE);
    subl(blocks, 1);
    movdqu(data, Address(input_data, 0));
    vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
    vpxor(state, state, data, Assembler::AVX_128bit);
    // gfmul(H*2, state)
    call(GFMUL, relocInfo::none);
    addptr(input_data, 16);
    jmp(PROCESS_1_BLOCK);

    bind(SAVE_STATE);
    vpshufb(state, state, lswap_mask, Assembler::AVX_128bit);
    movdqu(Address(input_state, 0), state);
    jmp(EXIT_GHASH);

    bind(GFMUL);
    gfmul(tmp0, state);

    bind(GENERATE_HTBL_1_BLK);
    generateHtbl_one_block(htbl);

    bind(GENERATE_HTBL_8_BLKS);
    generateHtbl_eight_blocks(htbl);

    bind(EXIT_GHASH);
    // zero out xmm registers used for Htbl storage
    vpxor(xmm0, xmm0, xmm0, Assembler::AVX_128bit);
    vpxor(xmm1, xmm1, xmm1, Assembler::AVX_128bit);
    vpxor(xmm3, xmm3, xmm3, Assembler::AVX_128bit);
    vpxor(xmm15, xmm15, xmm15, Assembler::AVX_128bit);
}

// Add 128-bit integers in xmmsrc1 to xmmsrc2, then place the result in xmmdst.
// Clobber ktmp and rscratch.
// Used by aesctr_encrypt.
void MacroAssembler::ev_add128(XMMRegister xmmdst, XMMRegister xmmsrc1, XMMRegister xmmsrc2,
                            int vector_len, KRegister ktmp, Register rscratch) {
  vpaddq(xmmdst, xmmsrc1, xmmsrc2, vector_len);
  evpcmpuq(ktmp, xmmdst, xmmsrc2, lt, vector_len); // set mask[0/1] bit if addq to dst[0/1] wraps
  kshiftlbl(ktmp, ktmp, 1);                        // mask[1] <- mask[0], mask[0] <- 0, etc

  evpaddq(xmmdst, ktmp, xmmdst, xmm17, /*merge*/true,
          vector_len);                             // dst[1]++ if mask[1] set
}

// AES Counter Mode using VAES instructions
void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Register key, Register counter,
    Register len_reg, Register used, Register used_addr, Register saved_encCounter_start) {

    const Register rounds = 0;
    const Register pos = r12;

    Label PRELOOP_START, EXIT_PRELOOP, REMAINDER, REMAINDER_16, LOOP, END, EXIT, END_LOOP,
    AES192, AES256, AES192_REMAINDER16, REMAINDER16_END_LOOP, AES256_REMAINDER16,
    REMAINDER_8, REMAINDER_4, AES192_REMAINDER8, REMAINDER_LOOP, AES256_REMINDER,
    AES192_REMAINDER, END_REMAINDER_LOOP, AES256_REMAINDER8, REMAINDER8_END_LOOP,
    AES192_REMAINDER4, AES256_REMAINDER4, AES256_REMAINDER, END_REMAINDER4, EXTRACT_TAILBYTES,
    EXTRACT_TAIL_4BYTES, EXTRACT_TAIL_2BYTES, EXTRACT_TAIL_1BYTE, STORE_CTR;

    cmpl(len_reg, 0);
    jcc(Assembler::belowEqual, EXIT);

    movl(pos, 0);
    // if the number of used encrypted counter bytes < 16,
    // XOR PT with saved encrypted counter to obtain CT
    bind(PRELOOP_START);
    cmpl(used, 16);
    jcc(Assembler::aboveEqual, EXIT_PRELOOP);
    movb(rbx, Address(saved_encCounter_start, used));
    xorb(rbx, Address(src_addr, pos));
    movb(Address(dest_addr, pos), rbx);
    addptr(pos, 1);
    addptr(used, 1);
    decrement(len_reg);
    jcc(Assembler::notEqual, PRELOOP_START);

    bind(EXIT_PRELOOP);
    movl(Address(used_addr, 0), used);

    cmpl(len_reg, 0);
    jcc(Assembler::equal, EXIT);

    // Calculate number of rounds i.e. 10, 12, 14,  based on key length(128, 192, 256).
    movl(rounds, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)));

    vpxor(xmm0, xmm0, xmm0, Assembler::AVX_128bit);
    // Move initial counter value in xmm0
    movdqu(xmm0, Address(counter, 0));
    // broadcast counter value to zmm8
    evshufi64x2(xmm8, xmm0, xmm0, 0, Assembler::AVX_512bit);

    // load lbswap mask
    evmovdquq(xmm16, ExternalAddress(StubRoutines::x86::counter_mask_addr()), Assembler::AVX_512bit, r15);

    //shuffle counter using lbswap_mask
    vpshufb(xmm8, xmm8, xmm16, Assembler::AVX_512bit);

    // Vector value to propagate carries
    evmovdquq(xmm17, ExternalAddress(StubRoutines::x86::counter_mask_ones_addr()), Assembler::AVX_512bit, r15);
    // pre-increment and propagate counter values to zmm9-zmm15 registers.
    // Linc0 increments the zmm8 by 1 (initial value being 0), Linc4 increments the counters zmm9-zmm15 by 4
    // The counter is incremented after each block i.e. 16 bytes is processed;
    // each zmm register has 4 counter values as its MSB
    // the counters are incremented in parallel
    evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 64), Assembler::AVX_512bit, r15 /*rscratch*/);//linc0
    ev_add128(xmm8, xmm8,   xmm19, Assembler::AVX_512bit,  /*ktmp*/k1, r15);
    evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15 /*rscratch*/);//linc4
    ev_add128(xmm9,   xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15);
    ev_add128(xmm10,  xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15);
    ev_add128(xmm11, xmm10, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15);
    ev_add128(xmm12, xmm11, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15);
    ev_add128(xmm13, xmm12, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15);
    ev_add128(xmm14, xmm13, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15);
    ev_add128(xmm15, xmm14, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15);

    // load linc32 mask in zmm register.linc32 increments counter by 32
    evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 256), Assembler::AVX_512bit, r15);//Linc32

    // xmm31 contains the key shuffle mask.
    movdqu(xmm31, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr()));
    // Load key function loads 128 bit key and shuffles it. Then we broadcast the shuffled key to convert it into a 512 bit value.
    // For broadcasting the values to ZMM, vshufi64 is used instead of evbroadcasti64x2 as the source in this case is ZMM register
    // that holds shuffled key value.
    ev_load_key(xmm20, key, 0, xmm31);
    ev_load_key(xmm21, key, 1 * 16, xmm31);
    ev_load_key(xmm22, key, 2 * 16, xmm31);
    ev_load_key(xmm23, key, 3 * 16, xmm31);
    ev_load_key(xmm24, key, 4 * 16, xmm31);
    ev_load_key(xmm25, key, 5 * 16, xmm31);
    ev_load_key(xmm26, key, 6 * 16, xmm31);
    ev_load_key(xmm27, key, 7 * 16, xmm31);
    ev_load_key(xmm28, key, 8 * 16, xmm31);
    ev_load_key(xmm29, key, 9 * 16, xmm31);
    ev_load_key(xmm30, key, 10 * 16, xmm31);

    // Process 32 blocks or 512 bytes of data
    bind(LOOP);
    cmpl(len_reg, 512);
    jcc(Assembler::less, REMAINDER);
    subq(len_reg, 512);
    //Shuffle counter and Exor it with roundkey1. Result is stored in zmm0-7
    vpshufb(xmm0, xmm8, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm1, xmm9, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm1, xmm1, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm2, xmm10, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm2, xmm2, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm3, xmm11, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm3, xmm3, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm4, xmm12, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm4, xmm4, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm5, xmm13, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm5, xmm5, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm6, xmm14, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm6, xmm6, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm7, xmm15, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm7, xmm7, xmm20, Assembler::AVX_512bit);
    // Perform AES encode operations and put results in zmm0-zmm7.
    // This is followed by incrementing counter values in zmm8-zmm15.
    // Since we will be processing 32 blocks at a time, the counter is incremented by 32.
    roundEnc(xmm21, 7);
    ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    roundEnc(xmm22, 7);
    ev_add128/*!!!*/(xmm9, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    roundEnc(xmm23, 7);
    ev_add128/*!!!*/(xmm10, xmm10, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    roundEnc(xmm24, 7);
    ev_add128/*!!!*/(xmm11, xmm11, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    roundEnc(xmm25, 7);
    ev_add128/*!!!*/(xmm12, xmm12, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    roundEnc(xmm26, 7);
    ev_add128/*!!!*/(xmm13, xmm13, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    roundEnc(xmm27, 7);
    ev_add128/*!!!*/(xmm14, xmm14, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    roundEnc(xmm28, 7);
    ev_add128/*!!!*/(xmm15, xmm15, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    roundEnc(xmm29, 7);

    cmpl(rounds, 52);
    jcc(Assembler::aboveEqual, AES192);
    lastroundEnc(xmm30, 7);
    jmp(END_LOOP);

    bind(AES192);
    roundEnc(xmm30, 7);
    ev_load_key(xmm18, key, 11 * 16, xmm31);
    roundEnc(xmm18, 7);
    cmpl(rounds, 60);
    jcc(Assembler::aboveEqual, AES256);
    ev_load_key(xmm18, key, 12 * 16, xmm31);
    lastroundEnc(xmm18, 7);
    jmp(END_LOOP);

    bind(AES256);
    ev_load_key(xmm18, key, 12 * 16, xmm31);
    roundEnc(xmm18, 7);
    ev_load_key(xmm18, key, 13 * 16, xmm31);
    roundEnc(xmm18, 7);
    ev_load_key(xmm18, key, 14 * 16, xmm31);
    lastroundEnc(xmm18, 7);

    // After AES encode rounds, the encrypted block cipher lies in zmm0-zmm7
    // xor encrypted block cipher and input plaintext and store resultant ciphertext
    bind(END_LOOP);
    evpxorq(xmm0, xmm0, Address(src_addr, pos, Address::times_1, 0 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 0), xmm0, Assembler::AVX_512bit);
    evpxorq(xmm1, xmm1, Address(src_addr, pos, Address::times_1, 1 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 64), xmm1, Assembler::AVX_512bit);
    evpxorq(xmm2, xmm2, Address(src_addr, pos, Address::times_1, 2 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 2 * 64), xmm2, Assembler::AVX_512bit);
    evpxorq(xmm3, xmm3, Address(src_addr, pos, Address::times_1, 3 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 3 * 64), xmm3, Assembler::AVX_512bit);
    evpxorq(xmm4, xmm4, Address(src_addr, pos, Address::times_1, 4 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 4 * 64), xmm4, Assembler::AVX_512bit);
    evpxorq(xmm5, xmm5, Address(src_addr, pos, Address::times_1, 5 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 5 * 64), xmm5, Assembler::AVX_512bit);
    evpxorq(xmm6, xmm6, Address(src_addr, pos, Address::times_1, 6 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 6 * 64), xmm6, Assembler::AVX_512bit);
    evpxorq(xmm7, xmm7, Address(src_addr, pos, Address::times_1, 7 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 7 * 64), xmm7, Assembler::AVX_512bit);
    addq(pos, 512);
    jmp(LOOP);

    // Encode 256, 128, 64 or 16 bytes at a time if length is less than 512 bytes
    bind(REMAINDER);
    cmpl(len_reg, 0);
    jcc(Assembler::equal, END);
    cmpl(len_reg, 256);
    jcc(Assembler::aboveEqual, REMAINDER_16);
    cmpl(len_reg, 128);
    jcc(Assembler::aboveEqual, REMAINDER_8);
    cmpl(len_reg, 64);
    jcc(Assembler::aboveEqual, REMAINDER_4);
    // At this point, we will process 16 bytes of data at a time.
    // So load xmm19 with counter increment value as 1
    evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 80), Assembler::AVX_128bit, r15);
    jmp(REMAINDER_LOOP);

    // Each ZMM register can be used to encode 64 bytes of data, so we have 4 ZMM registers to encode 256 bytes of data
    bind(REMAINDER_16);
    subq(len_reg, 256);
    // As we process 16 blocks at a time, load mask for incrementing the counter value by 16
    evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 320), Assembler::AVX_512bit, r15);//Linc16(rip)
    // shuffle counter and XOR counter with roundkey1
    vpshufb(xmm0, xmm8, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm1, xmm9, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm1, xmm1, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm2, xmm10, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm2, xmm2, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm3, xmm11, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm3, xmm3, xmm20, Assembler::AVX_512bit);
    // Increment counter values by 16
    ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    ev_add128/*!!!*/(xmm9, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    // AES encode rounds
    roundEnc(xmm21, 3);
    roundEnc(xmm22, 3);
    roundEnc(xmm23, 3);
    roundEnc(xmm24, 3);
    roundEnc(xmm25, 3);
    roundEnc(xmm26, 3);
    roundEnc(xmm27, 3);
    roundEnc(xmm28, 3);
    roundEnc(xmm29, 3);

    cmpl(rounds, 52);
    jcc(Assembler::aboveEqual, AES192_REMAINDER16);
    lastroundEnc(xmm30, 3);
    jmp(REMAINDER16_END_LOOP);

    bind(AES192_REMAINDER16);
    roundEnc(xmm30, 3);
    ev_load_key(xmm18, key, 11 * 16, xmm31);
    roundEnc(xmm18, 3);
    ev_load_key(xmm5, key, 12 * 16, xmm31);

    cmpl(rounds, 60);
    jcc(Assembler::aboveEqual, AES256_REMAINDER16);
    lastroundEnc(xmm5, 3);
    jmp(REMAINDER16_END_LOOP);
    bind(AES256_REMAINDER16);
    roundEnc(xmm5, 3);
    ev_load_key(xmm6, key, 13 * 16, xmm31);
    roundEnc(xmm6, 3);
    ev_load_key(xmm7, key, 14 * 16, xmm31);
    lastroundEnc(xmm7, 3);

    // After AES encode rounds, the encrypted block cipher lies in zmm0-zmm3
    // xor 256 bytes of PT with the encrypted counters to produce CT.
    bind(REMAINDER16_END_LOOP);
    evpxorq(xmm0, xmm0, Address(src_addr, pos, Address::times_1, 0), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 0), xmm0, Assembler::AVX_512bit);
    evpxorq(xmm1, xmm1, Address(src_addr, pos, Address::times_1, 1 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 1 * 64), xmm1, Assembler::AVX_512bit);
    evpxorq(xmm2, xmm2, Address(src_addr, pos, Address::times_1, 2 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 2 * 64), xmm2, Assembler::AVX_512bit);
    evpxorq(xmm3, xmm3, Address(src_addr, pos, Address::times_1, 3 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 3 * 64), xmm3, Assembler::AVX_512bit);
    addq(pos, 256);

    cmpl(len_reg, 128);
    jcc(Assembler::aboveEqual, REMAINDER_8);

    cmpl(len_reg, 64);
    jcc(Assembler::aboveEqual, REMAINDER_4);
    //load mask for incrementing the counter value by 1
    evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 80), Assembler::AVX_128bit, r15);//Linc0 + 16(rip)
    jmp(REMAINDER_LOOP);

    // Each ZMM register can be used to encode 64 bytes of data, so we have 2 ZMM registers to encode 128 bytes of data
    bind(REMAINDER_8);
    subq(len_reg, 128);
    // As we process 8 blocks at a time, load mask for incrementing the counter value by 8
    evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 192), Assembler::AVX_512bit, r15);//Linc8(rip)
    // shuffle counters and xor with roundkey1
    vpshufb(xmm0, xmm8, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_512bit);
    vpshufb(xmm1, xmm9, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm1, xmm1, xmm20, Assembler::AVX_512bit);
    // increment counter by 8
    ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    // AES encode
    roundEnc(xmm21, 1);
    roundEnc(xmm22, 1);
    roundEnc(xmm23, 1);
    roundEnc(xmm24, 1);
    roundEnc(xmm25, 1);
    roundEnc(xmm26, 1);
    roundEnc(xmm27, 1);
    roundEnc(xmm28, 1);
    roundEnc(xmm29, 1);

    cmpl(rounds, 52);
    jcc(Assembler::aboveEqual, AES192_REMAINDER8);
    lastroundEnc(xmm30, 1);
    jmp(REMAINDER8_END_LOOP);

    bind(AES192_REMAINDER8);
    roundEnc(xmm30, 1);
    ev_load_key(xmm18, key, 11 * 16, xmm31);
    roundEnc(xmm18, 1);
    ev_load_key(xmm5, key, 12 * 16, xmm31);
    cmpl(rounds, 60);
    jcc(Assembler::aboveEqual, AES256_REMAINDER8);
    lastroundEnc(xmm5, 1);
    jmp(REMAINDER8_END_LOOP);

    bind(AES256_REMAINDER8);
    roundEnc(xmm5, 1);
    ev_load_key(xmm6, key, 13 * 16, xmm31);
    roundEnc(xmm6, 1);
    ev_load_key(xmm7, key, 14 * 16, xmm31);
    lastroundEnc(xmm7, 1);

    bind(REMAINDER8_END_LOOP);
    // After AES encode rounds, the encrypted block cipher lies in zmm0-zmm1
    // XOR PT with the encrypted counter and store as CT
    evpxorq(xmm0, xmm0, Address(src_addr, pos, Address::times_1, 0 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 0 * 64), xmm0, Assembler::AVX_512bit);
    evpxorq(xmm1, xmm1, Address(src_addr, pos, Address::times_1, 1 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 1 * 64), xmm1, Assembler::AVX_512bit);
    addq(pos, 128);

    cmpl(len_reg, 64);
    jcc(Assembler::aboveEqual, REMAINDER_4);
    // load mask for incrementing the counter value by 1
    evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 80), Assembler::AVX_128bit, r15);//Linc0 + 16(rip)
    jmp(REMAINDER_LOOP);

    // Each ZMM register can be used to encode 64 bytes of data, so we have 1 ZMM register used in this block of code
    bind(REMAINDER_4);
    subq(len_reg, 64);
    // As we process 4 blocks at a time, load mask for incrementing the counter value by 4
    evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip)
    // XOR counter with first roundkey
    vpshufb(xmm0, xmm8, xmm16, Assembler::AVX_512bit);
    evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_512bit);
    // Increment counter
    ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/);
    vaesenc(xmm0, xmm0, xmm21, Assembler::AVX_512bit);
    vaesenc(xmm0, xmm0, xmm22, Assembler::AVX_512bit);
    vaesenc(xmm0, xmm0, xmm23, Assembler::AVX_512bit);
    vaesenc(xmm0, xmm0, xmm24, Assembler::AVX_512bit);
    vaesenc(xmm0, xmm0, xmm25, Assembler::AVX_512bit);
    vaesenc(xmm0, xmm0, xmm26, Assembler::AVX_512bit);
    vaesenc(xmm0, xmm0, xmm27, Assembler::AVX_512bit);
    vaesenc(xmm0, xmm0, xmm28, Assembler::AVX_512bit);
    vaesenc(xmm0, xmm0, xmm29, Assembler::AVX_512bit);
    cmpl(rounds, 52);
    jcc(Assembler::aboveEqual, AES192_REMAINDER4);
    vaesenclast(xmm0, xmm0, xmm30, Assembler::AVX_512bit);
    jmp(END_REMAINDER4);

    bind(AES192_REMAINDER4);
    vaesenc(xmm0, xmm0, xmm30, Assembler::AVX_512bit);
    ev_load_key(xmm18, key, 11 * 16, xmm31);
    vaesenc(xmm0, xmm0, xmm18, Assembler::AVX_512bit);
    ev_load_key(xmm5, key, 12 * 16, xmm31);

    cmpl(rounds, 60);
    jcc(Assembler::aboveEqual, AES256_REMAINDER4);
    vaesenclast(xmm0, xmm0, xmm5, Assembler::AVX_512bit);
    jmp(END_REMAINDER4);

    bind(AES256_REMAINDER4);
    vaesenc(xmm0, xmm0, xmm5, Assembler::AVX_512bit);
    ev_load_key(xmm6, key, 13 * 16, xmm31);
    vaesenc(xmm0, xmm0, xmm6, Assembler::AVX_512bit);
    ev_load_key(xmm7, key, 14 * 16, xmm31);
    vaesenclast(xmm0, xmm0, xmm7, Assembler::AVX_512bit);
    // After AES encode rounds, the encrypted block cipher lies in zmm0.
    // XOR encrypted block cipher with PT and store 64 bytes of ciphertext
    bind(END_REMAINDER4);
    evpxorq(xmm0, xmm0, Address(src_addr, pos, Address::times_1, 0 * 64), Assembler::AVX_512bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 0), xmm0, Assembler::AVX_512bit);
    addq(pos, 64);
    // load mask for incrementing the counter value by 1
    evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 80), Assembler::AVX_128bit, r15);//Linc0 + 16(rip)

    // For a single block, the AES rounds start here.
    bind(REMAINDER_LOOP);
    cmpl(len_reg, 0);
    jcc(Assembler::belowEqual, END);
    // XOR counter with first roundkey
    vpshufb(xmm0, xmm8, xmm16, Assembler::AVX_128bit);
    evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_128bit);
    vaesenc(xmm0, xmm0, xmm21, Assembler::AVX_128bit);
    // Increment counter by 1
    ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_128bit, /*ktmp*/k1, r15 /*rscratch*/);
    vaesenc(xmm0, xmm0, xmm22, Assembler::AVX_128bit);
    vaesenc(xmm0, xmm0, xmm23, Assembler::AVX_128bit);
    vaesenc(xmm0, xmm0, xmm24, Assembler::AVX_128bit);
    vaesenc(xmm0, xmm0, xmm25, Assembler::AVX_128bit);
    vaesenc(xmm0, xmm0, xmm26, Assembler::AVX_128bit);
    vaesenc(xmm0, xmm0, xmm27, Assembler::AVX_128bit);
    vaesenc(xmm0, xmm0, xmm28, Assembler::AVX_128bit);
    vaesenc(xmm0, xmm0, xmm29, Assembler::AVX_128bit);

    cmpl(rounds, 52);
    jcc(Assembler::aboveEqual, AES192_REMAINDER);
    vaesenclast(xmm0, xmm0, xmm30, Assembler::AVX_128bit);
    jmp(END_REMAINDER_LOOP);

    bind(AES192_REMAINDER);
    vaesenc(xmm0, xmm0, xmm30, Assembler::AVX_128bit);
    ev_load_key(xmm18, key, 11 * 16, xmm31);
    vaesenc(xmm0, xmm0, xmm18, Assembler::AVX_128bit);
    ev_load_key(xmm5, key, 12 * 16, xmm31);
    cmpl(rounds, 60);
    jcc(Assembler::aboveEqual, AES256_REMAINDER);
    vaesenclast(xmm0, xmm0, xmm5, Assembler::AVX_128bit);
    jmp(END_REMAINDER_LOOP);

    bind(AES256_REMAINDER);
    vaesenc(xmm0, xmm0, xmm5, Assembler::AVX_128bit);
    ev_load_key(xmm6, key, 13 * 16, xmm31);
    vaesenc(xmm0, xmm0, xmm6, Assembler::AVX_128bit);
    ev_load_key(xmm7, key, 14 * 16, xmm31);
    vaesenclast(xmm0, xmm0, xmm7, Assembler::AVX_128bit);

    bind(END_REMAINDER_LOOP);
    // If the length register is less than the blockSize i.e. 16
    // then we store only those bytes of the CT to the destination
    // corresponding to the length register value
    // extracting the exact number of bytes is handled by EXTRACT_TAILBYTES
    cmpl(len_reg, 16);
    jcc(Assembler::less, EXTRACT_TAILBYTES);
    subl(len_reg, 16);
    // After AES encode rounds, the encrypted block cipher lies in xmm0.
    // If the length register is equal to 16 bytes, store CT in dest after XOR operation.
    evpxorq(xmm0, xmm0, Address(src_addr, pos, Address::times_1, 0), Assembler::AVX_128bit);
    evmovdquq(Address(dest_addr, pos, Address::times_1, 0), xmm0, Assembler::AVX_128bit);
    addl(pos, 16);

    jmp(REMAINDER_LOOP);

    bind(EXTRACT_TAILBYTES);
    // Save encrypted counter value in xmm0 for next invocation, before XOR operation
    movdqu(Address(saved_encCounter_start, 0), xmm0);
    // XOR encryted block cipher in xmm0 with PT to produce CT
    evpxorq(xmm0, xmm0, Address(src_addr, pos, Address::times_1, 0), Assembler::AVX_128bit);
    // extract upto 15 bytes of CT from xmm0 as specified by length register
    testptr(len_reg, 8);
    jcc(Assembler::zero, EXTRACT_TAIL_4BYTES);
    pextrq(Address(dest_addr, pos), xmm0, 0);
    psrldq(xmm0, 8);
    addl(pos, 8);
    bind(EXTRACT_TAIL_4BYTES);
    testptr(len_reg, 4);
    jcc(Assembler::zero, EXTRACT_TAIL_2BYTES);
    pextrd(Address(dest_addr, pos), xmm0, 0);
    psrldq(xmm0, 4);
    addq(pos, 4);
    bind(EXTRACT_TAIL_2BYTES);
    testptr(len_reg, 2);
    jcc(Assembler::zero, EXTRACT_TAIL_1BYTE);
    pextrw(Address(dest_addr, pos), xmm0, 0);
    psrldq(xmm0, 2);
    addl(pos, 2);
    bind(EXTRACT_TAIL_1BYTE);
    testptr(len_reg, 1);
    jcc(Assembler::zero, END);
    pextrb(Address(dest_addr, pos), xmm0, 0);
    addl(pos, 1);

    bind(END);
    // If there are no tail bytes, store counter value and exit
    cmpl(len_reg, 0);
    jcc(Assembler::equal, STORE_CTR);
    movl(Address(used_addr, 0), len_reg);

    bind(STORE_CTR);
    //shuffle updated counter and store it
    vpshufb(xmm8, xmm8, xmm16, Assembler::AVX_128bit);
    movdqu(Address(counter, 0), xmm8);
    // Zero out counter and key registers
    evpxorq(xmm8, xmm8, xmm8, Assembler::AVX_512bit);
    evpxorq(xmm20, xmm20, xmm20, Assembler::AVX_512bit);
    evpxorq(xmm21, xmm21, xmm21, Assembler::AVX_512bit);
    evpxorq(xmm22, xmm22, xmm22, Assembler::AVX_512bit);
    evpxorq(xmm23, xmm23, xmm23, Assembler::AVX_512bit);
    evpxorq(xmm24, xmm24, xmm24, Assembler::AVX_512bit);
    evpxorq(xmm25, xmm25, xmm25, Assembler::AVX_512bit);
    evpxorq(xmm26, xmm26, xmm26, Assembler::AVX_512bit);
    evpxorq(xmm27, xmm27, xmm27, Assembler::AVX_512bit);
    evpxorq(xmm28, xmm28, xmm28, Assembler::AVX_512bit);
    evpxorq(xmm29, xmm29, xmm29, Assembler::AVX_512bit);
    evpxorq(xmm30, xmm30, xmm30, Assembler::AVX_512bit);
    cmpl(rounds, 44);
    jcc(Assembler::belowEqual, EXIT);
    evpxorq(xmm18, xmm18, xmm18, Assembler::AVX_512bit);
    evpxorq(xmm5, xmm5, xmm5, Assembler::AVX_512bit);
    cmpl(rounds, 52);
    jcc(Assembler::belowEqual, EXIT);
    evpxorq(xmm6, xmm6, xmm6, Assembler::AVX_512bit);
    evpxorq(xmm7, xmm7, xmm7, Assembler::AVX_512bit);
    bind(EXIT);
}

#endif // _LP64
