Каков наилучший способ выполнить разветвление с помощью Intel SSE? - PullRequest
6 голосов
/ 04 марта 2012

Я пишу компилятор, и мне нужно вывести код для условий ветвления для значений с плавающей запятой. Например, чтобы скомпилировать такой код:

if(a <= b){
    //1. DO something
} else {
    //2. Do something else
}

Когда a и b являются переменными с плавающей точкой. Мне просто нужно перейти к 2, если условие не соответствует действительности, иначе упасть до 1. Я рассматриваю здесь оптимизацию на уровне компилятора, учитывая, что в 1 и 2.

Мне нужно что-то, что работает со всеми операторами сравнения>,> =, <, <=, == и! = </p>

Я нашел способ сравнения - использовать CMPLTSD (и другие эквивалентные инструкции для других операторов отношений). Но с этим я должен использовать регистр SSE специально для результата, а затем я должен переместить его значение в регистр общего назначения (например, eax) и, наконец, сравнить значение с 0.

Я также видел, что инструкция UCOMISD должна правильно устанавливать флаги, но, видимо, она работает не так, как я думал.

Итак, как лучше всего обрабатывать такой код? Есть ли лучшие инструкции, чем первое решение, которое у меня есть?

Под лучшим я имею в виду общее решение этой проблемы. Если возможно, я бы хотел, чтобы код вел себя так же, как и при сравнении целых чисел (cmp a, b; метка jge). Конечно, я бы предпочел быстрые инструкции для достижения этой цели.

Ответы [ 2 ]

7 голосов
/ 05 марта 2012

Коды условий для ucomisd не соответствуют целочисленным кодам со знаком, но относятся к беззнаковым (с «неупорядоченным» в флаге четности).Признаюсь, это немного странно, но все четко задокументировано.Код, если вы действительно хотите разветвляться, может быть примерно таким для <=:

  ucomisd a,b
  ja else     ; greater
  jp else     ; unordered
  ; code for //1 goes here
  jmp end
else:
  ; code for //2 goes here
end:

Для <:

jae else   ; greater or equal
jp else    ; unordered

Я мог бы перечислить их все, если вы действительно хотитено вы можете просто посмотреть коды условий для ucomisd и сопоставить их с тем, что вам нужно.

2 голосов
/ 13 апреля 2017

Важно : ответ @ harold почти верный, но имеет тонкий неправильный аспект, который может привести вас в бешенство в очень важном случае - обработка NaN обратна большинству языков (например, c ++)).

Как правильно говорит @harold, неупорядоченный результат сравнения сохраняется во флаге четности.

Однако неупорядоченное сравнение имеет значение true, если какой-либо операнд равен NaN , как подробнов этот пост переполнения стека .Это означает, что NaN будет меньше, равно и больше абсолютно каждого числа , включая NaN.

Так что если вы хотите, чтобы ваш язык соответствовал языку c ++поведение, когда любое сравнение с NaN возвращает false, вы хотите:

Для <=:

ucomisd xmm0, xmm1
jbe else_label

Для <:

ucomisd xmm0, xmm1
jb else_label

Подтверждено впосле разборки gcc, где I return a >= b:

144e:       66 0f 2e c8             ucomisd %xmm0,%xmm1
1452:       0f 93 c0                setae  %al

Здесь используется setae, который является модификатором регистра, эквивалентным jae.Затем он немедленно возвращается, не проверяя флаг четности.

Почему его ja, а не jg, ответ @ harold по-прежнему является четким и правильным объяснением.

И, конечно, выне нужно использовать упорядоченное сравнение, вы можете использовать неупорядоченное сравнение, как показано в предыдущем ответе, если вы хотите, чтобы абсолютно все числа были меньше, больше, чем и равными NaN в вашей программе /язык (где даже NaN < NaN верно!).И, конечно, как вы можете видеть, он может быть немного медленнее, поскольку требует дополнительных проверок.

...