Умножение с помощью инструкций SSE/AVX

Последнее обновление: 03.07.2023
  • pmullw: перемножает слова из 8 дорожек, младшее слово результата помещается в итоговую дорожку

  • vpmullw: перемножает слова из 8 дорожек (128-битная версия) или из 16 дорожек (256-битная версия), младшее слово результата помещается в итоговую дорожку

  • pmulhuw: перемножает беззнаковые слова из 8 дорожек, старшее слово результата помещается в итоговую дорожку

  • vpmulhuw: перемножает слова из 8 дорожек (128-битная версия) или из 16 дорожек (256-битная версия), старшее слово результата помещается в итоговую дорожку

  • pmulhw: перемножает слова со знаком из 8 дорожек, старшее слово результата помещается в итоговую дорожку

  • vpmulhw: перемножает слова из 8 дорожек (128-битная версия) или из 16 дорожек (256-битная версия), старшее слово результата помещается в итоговую дорожку

  • pmulld: перемножает двойные слова (dword) из 4 дорожек, младшее двойное слово результата помещается в итоговую дорожку

    Схематично это выглядит так:

    Temp0[63:0] := SRC1[31:0] * SRC2[31:0]
    Temp1[63:0] := SRC1[63:32] * SRC2[63:32]
    Temp2[63:0] := SRC1[95:64] * SRC2[95:64]
    Temp3[63:0] := SRC1[127:96] * SRC2[127:96]
    DEST[31:0] := Temp0[31:0]
    DEST[63:32] := Temp1[31:0]
    DEST[95:64] := Temp2[31:0]
    DEST[127:96] := Temp3[31:0]
    DEST[MAXVL-1:128] := 0
    
  • vpmulld: перемножает двойные слова из 4 дорожек (128-битная версия) или из 8 дорожек (256-битная версия), младшее двойное слово результата помещается в итоговую дорожку

    Схематично это выглядит так (для 128-битной версии):

    Temp0[63:0] := SRC1[31:0] * SRC2[31:0]
    Temp1[63:0] := SRC1[63:32] * SRC2[63:32]
    Temp2[63:0] := SRC1[95:64] * SRC2[95:64]
    Temp3[63:0] := SRC1[127:96] * SRC2[127:96]
    DEST[31:0] := Temp0[31:0]
    DEST[63:32] := Temp1[31:0]
    DEST[95:64] := Temp2[31:0]
    DEST[127:96] := Temp3[31:0]
    DEST[MAXVL-1:128] := 0
    

    Для 256-разрядной версии

    Temp0[63:0] := SRC1[31:0] * SRC2[31:0]
    Temp1[63:0] := SRC1[63:32] * SRC2[63:32]
    Temp2[63:0] := SRC1[95:64] * SRC2[95:64]
    Temp3[63:0] := SRC1[127:96] * SRC2[127:96]
    Temp4[63:0] := SRC1[159:128] * SRC2[159:128]
    Temp5[63:0] := SRC1[191:160] * SRC2[191:160]
    Temp6[63:0] := SRC1[223:192] * SRC2[223:192]
    Temp7[63:0] := SRC1[255:224] * SRC2[255:224]
    DEST[31:0] := Temp0[31:0]
    DEST[63:32] := Temp1[31:0]
    DEST[95:64] := Temp2[31:0]
    DEST[127:96] := Temp3[31:0]
    DEST[159:128] := Temp4[31:0]
    DEST[191:160] := Temp5[31:0]
    DEST[223:192] := Temp6[31:0]
    DEST[255:224] := Temp7[31:0]
    DEST[MAXVL-1:256] := 0
    
  • vpmullq: перемножает четверные слова из 2 дорожек (128-битная версия) или из 4 дорожек (256-битная версия), младшее четверное слово результата помещается в итоговую дорожку

Проблема умножения дорожек связана с тем, что при умножении двух n-битных чисел результат надо помещать в дорожку, которая занимает n бит, хотя умножение n × n может дать результат 2×n бит. Таким образом, операция умножения по дорожкам создает проблемы, поскольку теряется переполнение. Подобные инструкции предполагают, что результат может занять 2*n бит, однако в итоговую дорожку регистра помещается только младшие или старшие n бит результата.

Синтаксис инструкций:

pmullw xmmdest, xmmsrc/mem128
vpmullw xmmdest, xmmsrc, xmm/mem128
vpmullw ymmdest, ymmsrc, ymm/mem256

pmulhuw xmmdest, xmmsrc/mem128
vpmulhuw xmmdest, xmmsrc, xmm/mem128
vpmulhuw ymmdest, ymmsrc, ymm/mem256

pmulhw xmmdest, xmmsrc/mem128
vpmulhw xmmdest, xmmsrc, xmm/mem128
vpmulhw ymmdest, ymmsrc, ymm/mem256

pmulld xmmdest, xmmsrc/mem128
vpmulld xmmdest, xmmsrc, xmm/mem128
vpmulld ymmdest, ymmsrc, ymm/mem256

vpmullq xmmdest, xmmsrc, xmm/mem128
vpmullq ymmdest, ymmsrc, ymm/mem256


Ряд инструкций позволяют обойти ограничения на разрядность результата

  • pmuldq: умножает двойные слова (dword) со знаком в 32-битных дорожках 0 и 2 и сохраняет 64-битные результаты в 64-битных дорожках 0 и 1 (они же 32-битные дорожки 0, 1, 2 и 3). Старшие 128 бит перекрывающего регистра YMM остаются без изменений

  • pmuludq: умножает беззнаковые двойные слова в 32-битных дорожках 0 и 2 и сохраняет 64-битные результаты в 64-битных дорожках 0 и 1 (они же 32-битные дорожки 0, 1, 2 и 3). Старшие 128 бит перекрывающего регистра YMM остаются без изменений

  • vpmuldq: 256-битные варианты умножает двойные слова со знаком в 32-битных дорожках 0, 2, 4, 6 и сохраняет 64-битные результаты в 64-битных дорожках 0, 1, 2 и 3 (они же 32-битные дорожки 0, 1, 2, 3 и 4, 5, 6, 7). 128-битная аналогична pmuldq за тем исключением, что старшие 128 бит перекрывающего регистра YMM заполняются нулями

  • vpmuludq: 256-битные варианты умножает беззнаковые двойные слова в 32-битных дорожках 0, 2, 4, 6 и сохраняет 64-битные результаты в 64-битных дорожках 0, 1, 2 и 3 (они же 32-битные дорожки 0, 1, 2, 3 и 4, 5, 6, 7). 128-битная аналогична pmuludq за тем исключением, что старшие 128 бит перекрывающего регистра YMM заполняются нулями

Синтаксис инструкций:

pmuldq xmmdest, xmmsrc/mem128
vpmuldq xmmdest, xmmsrc1, xmm/mem128
vpmuldq ymmdest, ymmsrc1, ymm/mem256

pmuludq xmmdest, xmmsrc/mem128
vpmuludq xmmdest, xmmsrc1, xmm/mem128
vpmuludq ymmdest, ymmsrc1, ymm/mem256

И еще пара инструкций - pclmulqdq и vpclmulqdq умножают четверные слова (qword) и сохраняют 128-битныq результат. pclmulqdq оставляет старшие 128 бит перекрывающего регистра YMM без изменений, а vpclmulqdq заполняет их нулями.

pclmulqdq xmmdest, xmmsrc/mem128, imm8
vpclmulqdq xmmdest, xmmsrc1, xmmsrc2/mem128, imm8

Операнд imm8 указывает, какие четверные слова использовать в качестве исходных операндов. На примере pclmulqdq:

imm8

Результат

00h

XMMdest = xmmdest[0-63] * XMM/mem128[0-63]

01h

XMMdest = xmmdest[64-127] * XMM/mem128[0-63]

10h

XMMdest = xmmdest[0-63] * XMM/mem128[64-127]

11h

XMMdest = xmmdest[64-127] * XMM/mem128[64-127]

vpclmulqdq работает аналогично, только числа берутся из второго и третьего операнда.

Пример умножения 128-разрядных чисел:

.data
    nums0 word 1, 2, 3, 4, 5, 6, 7, 8
    nums1 word 2, 3, 4, 5, 6, 7, 8, 9
.code
main proc 
    movdqa xmm0, oword ptr nums0 
    movdqa xmm1, oword ptr nums1
    pmullw xmm0, xmm1       ; XMM0 = XMM0 * XMM1 
    ; XMM0 = 2, 6, 12, 20, 30, 42, 56, 81
    ret
main endp
end
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850