/*

   BLIS
   An object-based framework for developing high-performance BLAS-like
   libraries.

   Copyright (C) 2014, The University of Texas at Austin

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are
   met:
    - Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    - Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    - Neither the name(s) of the copyright holder(s) nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#include "blis.h"

#include "bli_avx512_macros.h"

extern int32_t offsets[16];

void bli_dgemm_knl_asm_12x16
     (
       dim_t               k,
       double*    restrict alpha,
       double*    restrict a,
       double*    restrict b,
       double*    restrict beta,
       double*    restrict c, inc_t rs_c, inc_t cs_c,
       auxinfo_t* restrict data,
       cntx_t*    restrict cntx
     )
{
	//const void* a_next = bli_auxinfo_next_a( data );
	//const void* b_next = bli_auxinfo_next_b( data );

    const int32_t * offsetPtr = &offsets[0];

	__asm__ volatile
	(

    VPXORD(ZMM(8), ZMM(8), ZMM(8))  MOV(RAX, VAR(a))
    VMOVAPD(ZMM( 9), ZMM(8))        MOV(RBX, VAR(b))
    VMOVAPD(ZMM(10), ZMM(8))        //no ADD(RBX, IMM(4*64))
    VMOVAPD(ZMM(11), ZMM(8))        //maybe? PREFETCH(0, MEM(RAX, 0))
    VMOVAPD(ZMM(12), ZMM(8))        //maybe? PREFETCH(0, MEM(RAX,64))
    VMOVAPD(ZMM(13), ZMM(8))        VMOVAPD(ZMM(0), MEM(RBX,0*64))
    VMOVAPD(ZMM(14), ZMM(8))        VMOVAPD(ZMM(1), MEM(RBX,1*64))
    VMOVAPD(ZMM(15), ZMM(8))        MOV(RCX, VAR(c))
    VMOVAPD(ZMM(16), ZMM(8))        MOV(RDI, RCX)
    VMOVAPD(ZMM(17), ZMM(8))        VBROADCASTSS(ZMM(4), VAR(cs_c))
    VMOVAPD(ZMM(18), ZMM(8))        VMOVAPS(ZMM(5), VAR(offsetPtr))
    VMOVAPD(ZMM(19), ZMM(8))        VPMULLD(ZMM(4), ZMM(5), ZMM(4))
    VMOVAPD(ZMM(20), ZMM(8))        MOV(RDX, IMM(0xFFF))
    VMOVAPD(ZMM(21), ZMM(8))        KMOV(K(1), EDX)
    VMOVAPD(ZMM(22), ZMM(8))        KMOV(K(2), EDX)
    VMOVAPD(ZMM(23), ZMM(8))        KMOV(K(3), EDX)
    VMOVAPD(ZMM(24), ZMM(8))        VSCATTERPFDPS(0, MEM(RCX,ZMM(4),8,   0) MASK_K(1))
    VMOVAPD(ZMM(25), ZMM(8))        VSCATTERPFDPS(0, MEM(RCX,ZMM(4),8, 8*8) MASK_K(2))
    VMOVAPD(ZMM(26), ZMM(8))        VSCATTERPFDPS(0, MEM(RCX,ZMM(4),8,15*8) MASK_K(3))
    VMOVAPD(ZMM(27), ZMM(8))        MOV(RSI, VAR(k))
    VMOVAPD(ZMM(28), ZMM(8))        SAR(RSI, IMM(2)) // rsi = k/4
    VMOVAPD(ZMM(29), ZMM(8))
    VMOVAPD(ZMM(30), ZMM(8))
    VMOVAPD(ZMM(31), ZMM(8))

    JZ(.DCONSIDKLEFT)

    ALIGN16
    LABEL(.DLOOPKITER)

    VBROADCASTSD(ZMM(2), MEM(RAX, 0*8)) // Iteration 0
    VBROADCASTSD(ZMM(3), MEM(RAX, 1*8))
    VBROADCASTSD(ZMM(4), MEM(RAX, 2*8))
    VBROADCASTSD(ZMM(5), MEM(RAX, 3*8))
    VBROADCASTSD(ZMM(6), MEM(RAX, 4*8))
    VBROADCASTSD(ZMM(7), MEM(RAX, 5*8))
    VFMADD231PD(ZMM( 8), ZMM(0), ZMM(2))
    VFMADD231PD(ZMM( 9), ZMM(1), ZMM(2))
    VFMADD231PD(ZMM(10), ZMM(0), ZMM(3))
    VFMADD231PD(ZMM(11), ZMM(1), ZMM(3))
    VFMADD231PD(ZMM(12), ZMM(0), ZMM(4))
    VFMADD231PD(ZMM(13), ZMM(1), ZMM(4))
    VFMADD231PD(ZMM(14), ZMM(0), ZMM(5))
    VFMADD231PD(ZMM(15), ZMM(1), ZMM(5))
    VFMADD231PD(ZMM(16), ZMM(0), ZMM(6))
    VFMADD231PD(ZMM(17), ZMM(1), ZMM(6))
    VFMADD231PD(ZMM(18), ZMM(0), ZMM(7))
    VFMADD231PD(ZMM(19), ZMM(1), ZMM(7))

    VBROADCASTSD(ZMM(2), MEM(RAX,  6*8))
    VBROADCASTSD(ZMM(3), MEM(RAX,  7*8))
    VBROADCASTSD(ZMM(4), MEM(RAX,  8*8))
    VBROADCASTSD(ZMM(5), MEM(RAX,  9*8))
    VBROADCASTSD(ZMM(6), MEM(RAX, 10*8))
    VBROADCASTSD(ZMM(7), MEM(RAX, 11*8))
    VFMADD231PD(ZMM(20), ZMM(0), ZMM(2))
    VFMADD231PD(ZMM(21), ZMM(1), ZMM(2))
    VFMADD231PD(ZMM(22), ZMM(0), ZMM(3))
    VFMADD231PD(ZMM(23), ZMM(1), ZMM(3))
    VFMADD231PD(ZMM(24), ZMM(0), ZMM(4))
    VFMADD231PD(ZMM(25), ZMM(1), ZMM(4))
    VFMADD231PD(ZMM(26), ZMM(0), ZMM(5))
    VFMADD231PD(ZMM(27), ZMM(1), ZMM(5))
    VFMADD231PD(ZMM(28), ZMM(0), ZMM(6))
    VFMADD231PD(ZMM(29), ZMM(1), ZMM(6))
    VFMADD231PD(ZMM(30), ZMM(0), ZMM(7))
    VFMADD231PD(ZMM(31), ZMM(1), ZMM(7))

    VMOVAPD(ZMM(0), MEM(RBX,2*64))
    VMOVAPD(ZMM(1), MEM(RBX,3*64))

    PREFETCH(0, MEM(RAX, 64*8))
    PREFETCH(0, MEM(RAX, 72*8))

    VBROADCASTSD(ZMM(2), MEM(RAX, 12*8)) // Iteration 1
    VBROADCASTSD(ZMM(3), MEM(RAX, 13*8))
    VBROADCASTSD(ZMM(4), MEM(RAX, 14*8))
    VBROADCASTSD(ZMM(5), MEM(RAX, 15*8))
    VBROADCASTSD(ZMM(6), MEM(RAX, 16*8))
    VBROADCASTSD(ZMM(7), MEM(RAX, 17*8))
    VFMADD231PD(ZMM( 8), ZMM(0), ZMM(2))
    VFMADD231PD(ZMM( 9), ZMM(1), ZMM(2))
    VFMADD231PD(ZMM(10), ZMM(0), ZMM(3))
    VFMADD231PD(ZMM(11), ZMM(1), ZMM(3))
    VFMADD231PD(ZMM(12), ZMM(0), ZMM(4))
    VFMADD231PD(ZMM(13), ZMM(1), ZMM(4))
    VFMADD231PD(ZMM(14), ZMM(0), ZMM(5))
    VFMADD231PD(ZMM(15), ZMM(1), ZMM(5))
    VFMADD231PD(ZMM(16), ZMM(0), ZMM(6))
    VFMADD231PD(ZMM(17), ZMM(1), ZMM(6))
    VFMADD231PD(ZMM(18), ZMM(0), ZMM(7))
    VFMADD231PD(ZMM(19), ZMM(1), ZMM(7))

    VBROADCASTSD(ZMM(2), MEM(RAX, 18*8))
    VBROADCASTSD(ZMM(3), MEM(RAX, 19*8))
    VBROADCASTSD(ZMM(4), MEM(RAX, 20*8))
    VBROADCASTSD(ZMM(5), MEM(RAX, 21*8))
    VBROADCASTSD(ZMM(6), MEM(RAX, 22*8))
    VBROADCASTSD(ZMM(7), MEM(RAX, 23*8))
    VFMADD231PD(ZMM(20), ZMM(0), ZMM(2))
    VFMADD231PD(ZMM(21), ZMM(1), ZMM(2))
    VFMADD231PD(ZMM(22), ZMM(0), ZMM(3))
    VFMADD231PD(ZMM(23), ZMM(1), ZMM(3))
    VFMADD231PD(ZMM(24), ZMM(0), ZMM(4))
    VFMADD231PD(ZMM(25), ZMM(1), ZMM(4))
    VFMADD231PD(ZMM(26), ZMM(0), ZMM(5))
    VFMADD231PD(ZMM(27), ZMM(1), ZMM(5))
    VFMADD231PD(ZMM(28), ZMM(0), ZMM(6))
    VFMADD231PD(ZMM(29), ZMM(1), ZMM(6))
    VFMADD231PD(ZMM(30), ZMM(0), ZMM(7))
    VFMADD231PD(ZMM(31), ZMM(1), ZMM(7))

    VMOVAPD(ZMM(0), MEM(RBX,4*64))
    VMOVAPD(ZMM(1), MEM(RBX,5*64))

    PREFETCH(0, MEM(RAX, 80*8))
    PREFETCH(0, MEM(RAX, 88*8))

    VBROADCASTSD(ZMM(2), MEM(RAX, 24*8)) // Iteration 2
    VBROADCASTSD(ZMM(3), MEM(RAX, 25*8))
    VBROADCASTSD(ZMM(4), MEM(RAX, 26*8))
    VBROADCASTSD(ZMM(5), MEM(RAX, 27*8))
    VBROADCASTSD(ZMM(6), MEM(RAX, 28*8))
    VBROADCASTSD(ZMM(7), MEM(RAX, 29*8))
    VFMADD231PD(ZMM( 8), ZMM(0), ZMM(2))
    VFMADD231PD(ZMM( 9), ZMM(1), ZMM(2))
    VFMADD231PD(ZMM(10), ZMM(0), ZMM(3))
    VFMADD231PD(ZMM(11), ZMM(1), ZMM(3))
    VFMADD231PD(ZMM(12), ZMM(0), ZMM(4))
    VFMADD231PD(ZMM(13), ZMM(1), ZMM(4))
    VFMADD231PD(ZMM(14), ZMM(0), ZMM(5))
    VFMADD231PD(ZMM(15), ZMM(1), ZMM(5))
    VFMADD231PD(ZMM(16), ZMM(0), ZMM(6))
    VFMADD231PD(ZMM(17), ZMM(1), ZMM(6))
    VFMADD231PD(ZMM(18), ZMM(0), ZMM(7))
    VFMADD231PD(ZMM(19), ZMM(1), ZMM(7))

    VBROADCASTSD(ZMM(2), MEM(RAX, 30*8))
    VBROADCASTSD(ZMM(3), MEM(RAX, 31*8))
    VBROADCASTSD(ZMM(4), MEM(RAX, 32*8))
    VBROADCASTSD(ZMM(5), MEM(RAX, 33*8))
    VBROADCASTSD(ZMM(6), MEM(RAX, 34*8))
    VBROADCASTSD(ZMM(7), MEM(RAX, 35*8))
    VFMADD231PD(ZMM(20), ZMM(0), ZMM(2))
    VFMADD231PD(ZMM(21), ZMM(1), ZMM(2))
    VFMADD231PD(ZMM(22), ZMM(0), ZMM(3))
    VFMADD231PD(ZMM(23), ZMM(1), ZMM(3))
    VFMADD231PD(ZMM(24), ZMM(0), ZMM(4))
    VFMADD231PD(ZMM(25), ZMM(1), ZMM(4))
    VFMADD231PD(ZMM(26), ZMM(0), ZMM(5))
    VFMADD231PD(ZMM(27), ZMM(1), ZMM(5))
    VFMADD231PD(ZMM(28), ZMM(0), ZMM(6))
    VFMADD231PD(ZMM(29), ZMM(1), ZMM(6))
    VFMADD231PD(ZMM(30), ZMM(0), ZMM(7))
    VFMADD231PD(ZMM(31), ZMM(1), ZMM(7))

    VMOVAPD(ZMM(0), MEM(RBX,6*64))
    VMOVAPD(ZMM(1), MEM(RBX,7*64))

    ADD(RBX, IMM(4*8*16))
    PREFETCH(0, MEM(RAX, 96*8))
    PREFETCH(0, MEM(RAX, 104*8))

    VBROADCASTSD(ZMM(2), MEM(RAX, 36*8)) // Iteration 3
    VBROADCASTSD(ZMM(3), MEM(RAX, 37*8))
    VBROADCASTSD(ZMM(4), MEM(RAX, 38*8))
    VBROADCASTSD(ZMM(5), MEM(RAX, 39*8))
    VBROADCASTSD(ZMM(6), MEM(RAX, 40*8))
    VBROADCASTSD(ZMM(7), MEM(RAX, 41*8))
    VFMADD231PD(ZMM( 8), ZMM(0), ZMM(2))
    VFMADD231PD(ZMM( 9), ZMM(1), ZMM(2))
    VFMADD231PD(ZMM(10), ZMM(0), ZMM(3))
    VFMADD231PD(ZMM(11), ZMM(1), ZMM(3))
    VFMADD231PD(ZMM(12), ZMM(0), ZMM(4))
    VFMADD231PD(ZMM(13), ZMM(1), ZMM(4))
    VFMADD231PD(ZMM(14), ZMM(0), ZMM(5))
    VFMADD231PD(ZMM(15), ZMM(1), ZMM(5))
    VFMADD231PD(ZMM(16), ZMM(0), ZMM(6))
    VFMADD231PD(ZMM(17), ZMM(1), ZMM(6))
    VFMADD231PD(ZMM(18), ZMM(0), ZMM(7))
    VFMADD231PD(ZMM(19), ZMM(1), ZMM(7))

    VBROADCASTSD(ZMM(2), MEM(RAX, 42*8))
    VBROADCASTSD(ZMM(3), MEM(RAX, 43*8))
    VBROADCASTSD(ZMM(4), MEM(RAX, 44*8))
    VBROADCASTSD(ZMM(5), MEM(RAX, 45*8))
    VBROADCASTSD(ZMM(6), MEM(RAX, 46*8))
    VBROADCASTSD(ZMM(7), MEM(RAX, 47*8))
    VFMADD231PD(ZMM(20), ZMM(0), ZMM(2))
    VFMADD231PD(ZMM(21), ZMM(1), ZMM(2))
    VFMADD231PD(ZMM(22), ZMM(0), ZMM(3))
    VFMADD231PD(ZMM(23), ZMM(1), ZMM(3))
    VFMADD231PD(ZMM(24), ZMM(0), ZMM(4))
    VFMADD231PD(ZMM(25), ZMM(1), ZMM(4))
    VFMADD231PD(ZMM(26), ZMM(0), ZMM(5))
    VFMADD231PD(ZMM(27), ZMM(1), ZMM(5))
    VFMADD231PD(ZMM(28), ZMM(0), ZMM(6))
    VFMADD231PD(ZMM(29), ZMM(1), ZMM(6))
    VFMADD231PD(ZMM(30), ZMM(0), ZMM(7))
    VFMADD231PD(ZMM(31), ZMM(1), ZMM(7))

    ADD(RAX, IMM(4*8*12))
    SUB(RSI, IMM(1))

    VMOVAPD(ZMM(0), MEM(RBX,0*64))
    VMOVAPD(ZMM(1), MEM(RBX,1*64))

    JNZ(.DLOOPKITER)

    LABEL(.DCONSIDKLEFT)

    MOV(RSI, VAR(k))
    AND(RSI, IMM(3)) // rsi = k%4
    JZ(.DPOSTACCUM)

    ALIGN16
    LABEL(.DLOOPKLEFT)

    VBROADCASTSD(ZMM(2), MEM(RAX, 0*8))
    VBROADCASTSD(ZMM(3), MEM(RAX, 1*8))
    VBROADCASTSD(ZMM(4), MEM(RAX, 2*8))
    VBROADCASTSD(ZMM(5), MEM(RAX, 3*8))
    VBROADCASTSD(ZMM(6), MEM(RAX, 4*8))
    VBROADCASTSD(ZMM(7), MEM(RAX, 5*8))
    VFMADD231PD(ZMM( 8), ZMM(0), ZMM(2))
    VFMADD231PD(ZMM( 9), ZMM(1), ZMM(2))
    VFMADD231PD(ZMM(10), ZMM(0), ZMM(3))
    VFMADD231PD(ZMM(11), ZMM(1), ZMM(3))
    VFMADD231PD(ZMM(12), ZMM(0), ZMM(4))
    VFMADD231PD(ZMM(13), ZMM(1), ZMM(4))
    VFMADD231PD(ZMM(14), ZMM(0), ZMM(5))
    VFMADD231PD(ZMM(15), ZMM(1), ZMM(5))
    VFMADD231PD(ZMM(16), ZMM(0), ZMM(6))
    VFMADD231PD(ZMM(17), ZMM(1), ZMM(6))
    VFMADD231PD(ZMM(18), ZMM(0), ZMM(7))
    VFMADD231PD(ZMM(19), ZMM(1), ZMM(7))

    VBROADCASTSD(ZMM(2), MEM(RAX,  6*8))
    VBROADCASTSD(ZMM(3), MEM(RAX,  7*8))
    VBROADCASTSD(ZMM(4), MEM(RAX,  8*8))
    VBROADCASTSD(ZMM(5), MEM(RAX,  9*8))
    VBROADCASTSD(ZMM(6), MEM(RAX, 10*8))
    VBROADCASTSD(ZMM(7), MEM(RAX, 11*8))
    VFMADD231PD(ZMM(20), ZMM(0), ZMM(2))
    VFMADD231PD(ZMM(21), ZMM(1), ZMM(2))
    VFMADD231PD(ZMM(22), ZMM(0), ZMM(3))
    VFMADD231PD(ZMM(23), ZMM(1), ZMM(3))
    VFMADD231PD(ZMM(24), ZMM(0), ZMM(4))
    VFMADD231PD(ZMM(25), ZMM(1), ZMM(4))
    VFMADD231PD(ZMM(26), ZMM(0), ZMM(5))
    VFMADD231PD(ZMM(27), ZMM(1), ZMM(5))
    VFMADD231PD(ZMM(28), ZMM(0), ZMM(6))
    VFMADD231PD(ZMM(29), ZMM(1), ZMM(6))
    VFMADD231PD(ZMM(30), ZMM(0), ZMM(7))
    VFMADD231PD(ZMM(31), ZMM(1), ZMM(7))

    ADD(RAX, IMM(12*8))
    ADD(RBX, IMM(16*8))
    SUB(RSI, IMM(1))

    VMOVAPD(ZMM(0), MEM(RBX,0*64))
    VMOVAPD(ZMM(1), MEM(RBX,1*64))

    JNZ(.DLOOPKLEFT)

    LABEL(.DPOSTACCUM)

    MOV(RAX, VAR(alpha))
    MOV(RBX, VAR(beta))
    VBROADCASTSD(ZMM(0), MEM(RAX))
    VBROADCASTSD(ZMM(1), MEM(RBX))

    VMULPD(ZMM( 8), ZMM( 8), ZMM(0))
    VMULPD(ZMM( 9), ZMM( 9), ZMM(0))
    VMULPD(ZMM(10), ZMM(10), ZMM(0))
    VMULPD(ZMM(11), ZMM(11), ZMM(0))
    VMULPD(ZMM(12), ZMM(12), ZMM(0))
    VMULPD(ZMM(13), ZMM(13), ZMM(0))
    VMULPD(ZMM(14), ZMM(14), ZMM(0))
    VMULPD(ZMM(15), ZMM(15), ZMM(0))
    VMULPD(ZMM(16), ZMM(16), ZMM(0))
    VMULPD(ZMM(17), ZMM(17), ZMM(0))
    VMULPD(ZMM(18), ZMM(18), ZMM(0))
    VMULPD(ZMM(19), ZMM(19), ZMM(0))
    VMULPD(ZMM(20), ZMM(20), ZMM(0))
    VMULPD(ZMM(21), ZMM(21), ZMM(0))
    VMULPD(ZMM(22), ZMM(22), ZMM(0))
    VMULPD(ZMM(23), ZMM(23), ZMM(0))
    VMULPD(ZMM(24), ZMM(24), ZMM(0))
    VMULPD(ZMM(25), ZMM(25), ZMM(0))
    VMULPD(ZMM(26), ZMM(26), ZMM(0))
    VMULPD(ZMM(27), ZMM(27), ZMM(0))
    VMULPD(ZMM(28), ZMM(28), ZMM(0))
    VMULPD(ZMM(29), ZMM(29), ZMM(0))
    VMULPD(ZMM(30), ZMM(30), ZMM(0))
    VMULPD(ZMM(31), ZMM(31), ZMM(0))

    MOV(RDI, VAR(rs_c))
    SUB(RDI, IMM(1))
    JNZ(.DGENSTORED)

    LABEL(.ROWSTORED)

    MOV(RSI, VAR(cs_c))
    MOV(R(8), MEM(RBX))
    LEA(RSI, MEM(,RSI,8))
    LEA(RDX, MEM(RCX,RSI,4))
    LEA(RDI, MEM(RCX,RSI,8))
    LEA(R(13), MEM(RSI,RSI,2))
    SAL1(R(8)) // shift out the sign bit to check for +/- zero
    JZ(.DROWSTORBZ)

    VFMADD231PD(ZMM( 8), ZMM(1), MEM(RCX))
    VFMADD231PD(ZMM( 9), ZMM(1), MEM(RCX,64))
    VMOVUPD(MEM(RCX), ZMM( 8))
    VMOVUPD(MEM(RCX,64), ZMM( 9))

    VFMADD231PD(ZMM(10), ZMM(1), MEM(RCX,RSI,1))
    VFMADD231PD(ZMM(11), ZMM(1), MEM(RCX,RSI,1,64))
    VMOVUPD(MEM(RCX,RSI,1), ZMM(10))
    VMOVUPD(MEM(RCX,RSI,1,64), ZMM(11))

    VFMADD231PD(ZMM(12), ZMM(1), MEM(RCX,RSI,2))
    VFMADD231PD(ZMM(13), ZMM(1), MEM(RCX,RSI,2,64))
    VMOVUPD(MEM(RCX,RSI,2), ZMM(12))
    VMOVUPD(MEM(RCX,RSI,2,64), ZMM(13))

    VFMADD231PD(ZMM(14), ZMM(1), MEM(RCX,R(13),1))
    VFMADD231PD(ZMM(15), ZMM(1), MEM(RCX,R(13),1,64))
    VMOVUPD(MEM(RCX,R(13),1), ZMM(14))
    VMOVUPD(MEM(RCX,R(13),1,64), ZMM(15))

    VFMADD231PD(ZMM(16), ZMM(1), MEM(RDX))
    VFMADD231PD(ZMM(17), ZMM(1), MEM(RDX,64))
    VMOVUPD(MEM(RDX), ZMM(16))
    VMOVUPD(MEM(RDX,64), ZMM(17))

    VFMADD231PD(ZMM(18), ZMM(1), MEM(RDX,RSI,1))
    VFMADD231PD(ZMM(19), ZMM(1), MEM(RDX,RSI,1,64))
    VMOVUPD(MEM(RDX,RSI,1), ZMM(18))
    VMOVUPD(MEM(RDX,RSI,1,64), ZMM(19))

    VFMADD231PD(ZMM(20), ZMM(1), MEM(RDX,RSI,2))
    VFMADD231PD(ZMM(21), ZMM(1), MEM(RDX,RSI,2,64))
    VMOVUPD(MEM(RDX,RSI,2), ZMM(20))
    VMOVUPD(MEM(RDX,RSI,2,64), ZMM(21))

    VFMADD231PD(ZMM(22), ZMM(1), MEM(RDX,R(13),1))
    VFMADD231PD(ZMM(23), ZMM(1), MEM(RDX,R(13),1,64))
    VMOVUPD(MEM(RDX,R(13),1), ZMM(22))
    VMOVUPD(MEM(RDX,R(13),1,64), ZMM(23))

    VFMADD231PD(ZMM(24), ZMM(1), MEM(RDI))
    VFMADD231PD(ZMM(25), ZMM(1), MEM(RDI,64))
    VMOVUPD(MEM(RDI), ZMM(24))
    VMOVUPD(MEM(RDI,64), ZMM(25))

    VFMADD231PD(ZMM(26), ZMM(1), MEM(RDI,RSI,1))
    VFMADD231PD(ZMM(27), ZMM(1), MEM(RDI,RSI,1,64))
    VMOVUPD(MEM(RDI,RSI,1), ZMM(26))
    VMOVUPD(MEM(RDI,RSI,1,64), ZMM(27))

    VFMADD231PD(ZMM(28), ZMM(1), MEM(RDI,RSI,2))
    VFMADD231PD(ZMM(29), ZMM(1), MEM(RDI,RSI,2,64))
    VMOVUPD(MEM(RDI,RSI,2), ZMM(28))
    VMOVUPD(MEM(RDI,RSI,2,64), ZMM(29))

    VFMADD231PD(ZMM(30), ZMM(1), MEM(RDI,R(13),1))
    VFMADD231PD(ZMM(31), ZMM(1), MEM(RDI,R(13),1,64))
    VMOVUPD(MEM(RDI,R(13),1), ZMM(30))
    VMOVUPD(MEM(RDI,R(13),1,64), ZMM(31))

    JMP(.DDONE)

    LABEL(.DROWSTORBZ)

    VMOVUPD(MEM(RCX), ZMM( 8))
    VMOVUPD(MEM(RCX,64), ZMM( 9))
    VMOVUPD(MEM(RCX,RSI,1), ZMM(10))
    VMOVUPD(MEM(RCX,RSI,1,64), ZMM(11))
    VMOVUPD(MEM(RCX,RSI,2), ZMM(12))
    VMOVUPD(MEM(RCX,RSI,2,64), ZMM(13))
    VMOVUPD(MEM(RCX,R(13),1), ZMM(14))
    VMOVUPD(MEM(RCX,R(13),1,64), ZMM(15))
    VMOVUPD(MEM(RDX), ZMM(16))
    VMOVUPD(MEM(RDX,64), ZMM(17))
    VMOVUPD(MEM(RDX,RSI,1), ZMM(18))
    VMOVUPD(MEM(RDX,RSI,1,64), ZMM(19))
    VMOVUPD(MEM(RDX,RSI,2), ZMM(20))
    VMOVUPD(MEM(RDX,RSI,2,64), ZMM(21))
    VMOVUPD(MEM(RDX,R(13),1), ZMM(22))
    VMOVUPD(MEM(RDX,R(13),1,64), ZMM(23))
    VMOVUPD(MEM(RDI), ZMM(24))
    VMOVUPD(MEM(RDI,64), ZMM(25))
    VMOVUPD(MEM(RDI,RSI,1), ZMM(26))
    VMOVUPD(MEM(RDI,RSI,1,64), ZMM(27))
    VMOVUPD(MEM(RDI,RSI,2), ZMM(28))
    VMOVUPD(MEM(RDI,RSI,2,64), ZMM(29))
    VMOVUPD(MEM(RDI,R(13),1), ZMM(30))
    VMOVUPD(MEM(RDI,R(13),1,64), ZMM(31))

    JMP(.DDONE)

    LABEL(.DGENSTORED)

    MOV(RDI, VAR(cs_c))
    MOV(RAX, VAR(rs_c))
    LEA(RDI, MEM(,RDI,8))
    MOV(R(8), MEM(RBX))
    VBROADCASTSS(YMM(4), VAR(rs_c))
    VMOVAPS(YMM(5), VAR(offsetPtr))
    VPMULLD(YMM(4), YMM(5), YMM(4))
    LEA(RDX, MEM(RCX,RAX,8))
    MOV(RSI, 0x3F)
    SAL1(R(8)) // shift out the sign bit to check for +/- zero
    JZ(.DGENSTORBZ)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VGATHERDPD(ZMM(2) MASK_K(1), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(2), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM( 8))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM( 9))
    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM( 8))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM( 9))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VGATHERDPD(ZMM(2) MASK_K(3), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(4), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(10))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(11))
    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(3), ZMM(10))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(4), ZMM(11))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VGATHERDPD(ZMM(2) MASK_K(1), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(2), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(12))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(13))
    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(12))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(13))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VGATHERDPD(ZMM(2) MASK_K(3), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(4), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(14))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(15))
    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(3), ZMM(14))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(4), ZMM(15))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VGATHERDPD(ZMM(2) MASK_K(1), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(2), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(16))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(17))
    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(16))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(17))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VGATHERDPD(ZMM(2) MASK_K(3), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(4), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(18))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(19))
    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(3), ZMM(18))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(4), ZMM(19))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VGATHERDPD(ZMM(2) MASK_K(1), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(2), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(20))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(21))
    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(20))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(21))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VGATHERDPD(ZMM(2) MASK_K(3), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(4), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(22))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(23))
    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(3), ZMM(22))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(4), ZMM(23))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VGATHERDPD(ZMM(2) MASK_K(1), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(2), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(24))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(25))
    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(24))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(25))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VGATHERDPD(ZMM(2) MASK_K(3), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(4), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(26))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(27))
    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(3), ZMM(26))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(4), ZMM(27))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VGATHERDPD(ZMM(2) MASK_K(1), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(2), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(28))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(29))
    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(28))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(29))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VGATHERDPD(ZMM(2) MASK_K(3), MEM(RCX,YMM(4),8))
    VGATHERDPD(ZMM(3) MASK_K(4), MEM(RDX,YMM(4),8))
    VFMADD231PD(ZMM(2), ZMM(1), ZMM(30))
    VFMADD231PD(ZMM(3), ZMM(1), ZMM(31))
    KMOV(K(3), ESI)
    KMOV(K(4), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(3), ZMM(30))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(4), ZMM(31))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    JMP(.DDONE)

    LABEL(.DGENSTORBZ)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM( 8))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM( 9))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(10))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(11))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(12))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(13))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(14))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(15))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(16))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(17))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(18))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(19))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(20))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(21))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(22))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(23))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(24))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(25))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(26))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(27))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(28))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(29))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    KMOV(K(1), ESI)
    KMOV(K(2), ESI)
    VSCATTERDPD(MEM(RCX,YMM(4),8) MASK_K(1), ZMM(30))
    VSCATTERDPD(MEM(RDX,YMM(4),8) MASK_K(2), ZMM(31))
    ADD(RCX, RDI)
    ADD(RDX, RDI)

    LABEL(.DDONE)

	: // output operands (none)
    : // input operands
      [k]         "m" (k),
      [a]         "m" (a),
      [b]         "m" (b),
      [alpha]     "m" (alpha),
      [beta]      "m" (beta),
      [c]         "m" (c),
      [rs_c]      "m" (rs_c),
      [cs_c]      "m" (cs_c),
      //[a_next]    "m" (a_next),
      //[b_next]    "m" (b_next),
      [offsetPtr] "m" (offsetPtr)
    : // register clobber list
      "rax", "rbx", "rcx", "rdx", "rdi", "rsi", "r8", "r9", "r10", "r11", "r12",
      "r13", "r14", "r15", "zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5",
      "zmm6", "zmm7", "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13",
      "zmm14", "zmm15", "zmm16", "zmm17", "zmm18", "zmm19", "zmm20", "zmm21",
      "zmm22", "zmm23", "zmm24", "zmm25", "zmm26", "zmm27", "zmm28", "zmm29",
      "zmm30", "zmm31", "memory"
	);
}
