Сравнение векторов SIMD do дает векторы с результатами 0 / -1. Это относится к x86 MMX / SSE / AVX, ARM NEON, PowerPC Altivec и т. Д. (Они2 машины дополнения, поэтому мне нравится писать -1
вместо ~0
для представления элементов, состоящих из всех нулей / всех единичных битов.
например, pcmpeqd xmm0, xmm1
заменяет каждый элемент xmm0
на xmm0[i] == xmm1[i] ? -1 : 0;
Это позволяет использовать их в качестве масок AND , поскольку SIMD-код не может ветвиться отдельно на каждом элементе вектора без распаковки вскаляр и обратно.Это должно быть без ответвлений. Как использовать, если условие во встроенных функциях
например, чтобы смешать 2 вектора на основе условия, без SSE4.1 pblendvb
/ blendvps
, вы бы сравнили, а затем AND / ANDNOT/ ИЛИ ЖЕ.например, от Заменить байт другим
__m128i mask = _mm_cmpeq_epi8(inp, val); // movdqa xmm1, xmm0 / PCMPEQB xmm1, xmm2
// zero elements in the original where there was a match (that we want to replace)
inp = _mm_andnot_si128(mask, inp); // inp &= ~mask; // PANDN xmm0, xmm1
// zero elements where we keep the original
__m128i tmp = _mm_and_si128(newvals, mask); // newvals & mask; // PAND xmm3, xmm1
inp = _mm_or_si128(inp, tmp); // POR xmm0, xmm1
Но если вы хотите посчитать совпадения, вы можете вычесть результат сравнения. total -= -1
избегает необходимостиотрицать элементы вектора. Как посчитать вхождения символов, используя SIMD
Или для условного добавления чего-либо, вместо фактического смешивания, просто выполните total += (x & mask)
, потому что 0
является элементом идентификации для таких операций, как ADD (инекоторые другие, такие как XOR и OR).
См. Как получить доступ к массиву символов и изменить строчные буквы в верхний регистр, и наоборот и Преобразование строки в C ++ в верхнийСлучай для примеров в C с внутренними компонентами и ассемблером x86.
Все это не имеет отношения к ничего с операторами C и неявным преобразованием из логического значения в целое.
В C и C ++ операторы возвращают логическое условие true / false, которое в asm для большинства машин для скалярного кода (не автоматически векторизованного) отображается в бит в регистре флагов.
Преобразование этого числа в регистр - это совершенно отдельная вещь.
Но забавный факт: MIPS не имеет регистра флагов : он имеет некоторое сравнение -и ветвь инструкции для простогоТакие условия, как reg == reg
или reg != reg
(beq и bne).И ветвь на меньше нуля (ветвь на знаковом бите одного регистра): bltz $reg, target
.
(И архитектурный регистр $zero
, который всегда читается как ноль, так что вы можете использовать эту ветвь реализацииесли reg! = 0 или reg == 0).
Для более сложных условий для сравнения используйте slt
(установлен на меньше чем) или sltu
(установлен на меньше чем без знака)в регистр целых чисел.Как slt $t4, $t1, $t0
реализует t4 = t1 < t0
, производя 0 или 1. Затем вы можете переходить на это значение, равное 0 или нет, или комбинировать несколько условий с логическим И / ИЛИ, прежде чем переходить на это.Если один из ваших входных данных является действительным bool
, который уже равен 0 или 1, его можно оптимизировать в этом без slt.
Неполный список инструкций классических инструкций MIPS (не включая псевдоинструкции, такие как blt
)которые собираются в slt
в $at
+ bne
: http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html
Но MIPS32r6 / MIPS64r6 изменил это: инструкции, генерирующие значения истинности, теперь генерируют все нули или все единицы вместо просто очистки /установка 0-битового , в соответствии с https://en.wikipedia.org/wiki/MIPS_architecture#MIPS32/MIPS64_Release_6. MIPS32 / 64 r6 не двоично совместима с предыдущими ISA MIPS, она также переставила некоторые коды операций. И из-за этого изменения, даже не совместимы с источником asm! Но этоопределенное изменение к лучшему.
Интересный факт, есть недокументированная инструкция SALC 8086 (установите AL из переноса) , которая по-прежнему поддерживается в 16/32-битном режиме современнымиПроцессоры Intel (и AMD?).
Это в основном похоже на sbb al,al
без установки флагов: AL = CF? -1: 0. http://os2museum.com/wp/undocumented-8086-opcodes.
Вычитать с заимствованием с помощью saМой ввод дважды делает x-x - CF
на x86, где CF является заимствованием для вычитания.И x-x
конечно всегда ноль.(На некоторых других ISA, таких как ARM, значение флага переноса противоположно для вычитания, C set означает «без заимствования».)
В общем, вы можете сделать sbb edx,edx
(или любой другой регистр, который хотите), чтобы преобразовать CF в целое число 0 / -1. Но это работает только для CF; Флаг переноса является особенным, и нет ничего эквивалентного другим флагам.
Некоторые процессоры AMD даже распознают sbb same,same
как независимое от старого значения регистра, зависящее только от CF, например, обнуление xor. На других процессорах это все еще имеет тот же архитектурный эффект, но с микроархитектурной ложной зависимостью от старого значения EDX.