Растровое преобразование LLVM из вектора bool (i1) в i8, i16, et c. хорошо определено? - PullRequest
0 голосов
/ 07 мая 2020

Можно ли в LLVM преобразовать значение типа <8 x i1> в битовое преобразование в i8? Если да, то каков ожидаемый битовый порядок? Документация LLVM по bitcast не содержит явных указаний на это. Заявление:

Инструкция bitcast преобразует value в тип ty2. Это всегда безоперационное приведение , потому что при этом преобразовании биты не меняются. Преобразование выполняется так, как если бы значение было сохранено в памяти и считано обратно как тип ty2.

По касательной, в списке рассылки было разъяснено, что no-op cast не означает, как звучит . Возвращаясь к рассматриваемой проблеме, проблема, которую я вижу при преобразовании <8 x i1> в любой другой тип (не только i8), заключается в том, что значение типа <8 x i1> не может быть сохранено в памяти. Я подтвердил это экспериментально (код не включен), и это также хорошо задокументировано в списке рассылки . Поскольку сохранение значений типа <8 x i1> приводит к неопределенному поведению, спецификация «как если бы значение было сохранено в памяти и считано как тип ty2» подразумевает, что любое преобразование битов в или из <8 x i1> приводит к неопределенному поведению. Обратите внимание, что очень похожий вопрос задавался ранее, но ответы на этот вопрос не дают удовлетворительного ответа на общую проблему надежности, представленную здесь. Автор вышеупомянутой проблемы решил проблему, преобразовав <8 x i1> в <1 x i8>, но это приведение включает аргумент типа <8 x i1>, поэтому я не уверен, что это правильно.

Для чего это Стоит отметить, что в некоторых из моих небольших тестов с LLVM я подтвердил, что растровое преобразование от <8 x i1> до i8 работает. Ниже приведена функция, которая проверяет 8 i16 с за один раз на предмет того, равны ли они 42.

; Filename is equality-8x16.ll
define void @equals42(<8 x i16>* %src0,i8* %dst0,i64 %len0) { ; i32()*
entry:
    %len = udiv exact i64 %len0, 8
    br label %cond
cond:
    %i = phi i64 [ 0, %entry ], [ %isucc, %loop ]
    %src = phi <8 x i16>* [ %src0, %entry ], [ %srcsucc, %loop ]
    %dst = phi i8* [ %dst0, %entry ], [ %dstsucc, %loop ]
    %cmp = icmp slt i64 %i, %len
    br i1 %cmp, label %loop, label %end
loop:
    %isucc = add i64 %i, 1
    %srcsucc = getelementptr <8 x i16>, <8 x i16>* %src, i64 1
    %dstsucc = getelementptr i8, i8* %dst, i64 1
    %val = load <8 x i16>, <8 x i16>* %src
    %bits = icmp eq <8 x i16> %val, <i16 42,i16 42,i16 42,i16 42,i16 42,i16 42,i16 42,i16 42>
    %res = bitcast <8 x i1> %bits to i8
    store i8 %res, i8* %dst
    br label %cond
end:
    ret void;
}

А вот код C (call-equality.c), который его вызывает:

#include <stdio.h>
#include <stdint.h>

#define SZ 8

void equals42(void*,void*,int64_t);

/* Prints the highest bit first and the lowest bit last */
void printbits(uint8_t x)
{
    for(int i=sizeof(x)<<3; i; i--)
        putchar('0'+((x>>(i-1))&1));
}

int main(){
  uint16_t a[SZ * 8] = {0};
  uint8_t b[8];
  a[1] = 42;
  a[15] = 42;
  equals42(a,b,SZ * 8);
  for(int i = 0; i < SZ; i++){
    printf("Index %d:",i);
    printbits(b[i]);
    printf("\n");
  }
}

Сборка, компоновка и запуск с:

llc-9 -O3 -mcpu=skylake -filetype=obj equality-8x16.ll
gcc call-equality.c equality-8x16.o
./a.out

И вот результаты:

Index 0:00000010
Index 1:10000000
Index 2:00000000
Index 3:00000000
Index 4:00000000
Index 5:00000000
Index 6:00000000
Index 7:00000000

Это работает, и даже случается, что Я жду. Эти биты в позициях 1 и 15 устанавливаются (интерпретация байта 1, битовой позиции 7 как битовой позиции 15). Однако неясно, получу ли я те же результаты на платформе с прямым порядком байтов (я использую процессор Skylake с прямым порядком байтов). Опять же, я хотел бы подчеркнуть, что официальная документация LLVM не документирует поведение битовых трансляций с участием <8 x i1>.

Вопрос не только в том, «работает ли это на вашем компьютере или на моем». (Хотя, если у кого-то есть платформа с прямым порядком байтов, мне было бы любопытно посмотреть, дает ли примерная программа такие же результаты). Настоящие вопросы:

  • Существует ли какой-нибудь квазиавторитетный источник, даже если это просто потоки списков рассылки и средства отслеживания проблем, который определяет семантику этих биткастов?
  • Если эти биткасты ненадежны, каков идиоматический способ c преобразовать <8 x i1> в i8? Можно спроецировать все восемь бит по отдельности (через extractelement), а затем построить i8 с некоторыми операциями OR и битовыми сдвигами, но это кажется утомительным и в значительной степени зависит от прохода оптимизации для получения ожидаемой операции перемешивания. Есть что-нибудь получше?

1 Ответ

0 голосов
/ 07 мая 2020

Ближайшее, что я нашел до сих пор, - это ветка списка рассылки от 2018 года, когда пользователь замечает проблему, когда bitcast <16 x i1> %a1 to i16 плохо оптимизирован. Сопровождающий отвечает, предлагая исправление в r348104 (которое я не могу найти на GitHub или Phabricator). Но это, похоже, означает, что bitcast <16 x i1> %a1 to i16 понимается как хорошо определенное. Но что это на самом деле должно означать? Должен ли элемент 0 быть битовой позицией 0 в результирующем слове? Я так думаю, но было бы неплохо увидеть это где-нибудь прописанным.

...