2016-05-28 5 views
0

У нас есть уверенные стрельбы в сборках Debug, которые проверяют выравнивание. Утверждение относится к массиву байтов, который загружается в uint8x16_t с использованием vld1q_u8. В то время как утвердительные пожары, мы не наблюдали SIG_BUS.Требования к выравниванию для uint8x16_t, загружаемого из массива байтов?

Вот использование в коде:

const byte* input = ...; 
... 

assert(IsAlignedOn(input, GetAlignmentOf(uint8x16_t)); 
uint64x2_t message = vreinterpretq_u64_u8(vld1q_u8(input)); 

Я также попытался с нижеследующим, и утверждают, пожары для выравнивания uint8_t*:

assert(IsAlignedOn(input, GetAlignmentOf(uint8_t*)); 
uint64x2_t message = vreinterpretq_u64_u8(vld1q_u8(input)); 

Каковы требования к выравниванию для массива байтов при загрузке в uint8x16_t с vld1q_u8?


В приведенном выше коде input является параметром функции. IsAlignedOn проверяет выравнивание двух своих аргументов, гарантируя, что первое выровнено по меньшей мере на второе. GetAlignmentOf - это абстракция, которая извлекает выравнивание для типа или переменной.

uint8x16_t и uint64x2_t - 128-битные векторные типы данных ARM NEON, которые являются expected to be placed in a Q register. vld1q_u8 является инструкцией NEON, которая, как ожидается, будет скомпилирована в инструкцию VLD1.8. vreinterpretq_u64_u8 - это псевдо-инструкция NEON, которая упрощает использование типов данных.

+0

Код не C. – Olaf

+0

@Olaf - Я не уверен, что вы правы. Они являются внутренними, [которые являются расширением языка C] (http://gcc.gnu.org/onlinedocs/gcc/ARM-C-Language-Extensions-_0028ACLE_0029.html). Приведенный документ GCC ссылается на документы ARM, поэтому вы должны иметь обе ссылки, если хотите прочитать о них. – jww

+0

Просьба указать ссылку, где стандарт C позволяет использовать синтаксис типа GetAlignmentOf '! Повторите свое редактирование: укажите [mcve] с объявлением переменной 'uint8x16_t'. И выравнивание байтового массива определяется стандартом «1». – Olaf

ответ

3

Естественное выравнивание инструкции VLD1.8, загрузка 16 байтов в регистр Quad, является байтом. Это означает, что даже если непереведенные передачи не разрешены, эта инструкция не может быть неисправна.

Таким образом, похоже, что это конкретное утверждение неверно.

+0

Хотя 'VLD1.8 ..., [Rn: 64]' может, конечно, ошибиться даже при нормальной неравномерной модели доступа. – Notlikethat

+0

@ Dric512 - Это 'byte' или' byte * '? Я думаю, что разница составляет 1 и 4. На данный момент, обо всем, что я знаю, это не «uint8x16_t», потому что я не вижу «SIG_BUS» из-за отсутствия 16-байтных выравниваний. Я проверил в [патче, чтобы отменить утверждение на 'uint8_t *'] (http://github.com/weidai11/cryptopp/commit/b86f3fef8716436705b2963baea350beebb1d790), поэтому я должен вскоре получить некоторые результаты из [нашего тестового скрипта] (http://github.com/weidai11/cryptopp/blob/arm-neon/cryptest.sh). – jww

+0

Обычно для оптимизации кода используется такая оптимизация. Неуправляемые обращения на самом деле делают opisite на многих платформах. Например. они могут быть разбиты на доступ для доступа по 1/2/4/... байта. – Olaf

4

При написании прямого ассемблера (встроенного или во внешних файлах) вы можете выбрать, нужно ли указывать выравнивание (например, vld1.8 {q0}, [r0, :64]) или оставить его (например, vld1.8 {q0}, [r0]). Если он не указан, он не требует какого-либо конкретного выравнивания вообще, как говорит Dric512.

При использовании vld1q_u8 с помощью встроенных функций вы никогда не указываете выравнивание, насколько мне известно, компилятор не принимает его и выдает инструкцию без спецификации выравнивания. Я не уверен, что некоторые компиляторы могут вывести некоторые случаи, когда выравнивание фактически гарантировано, и использовать спецификатор выравнивания в этих случаях. (Как gcc, clang, так и MSVC, похоже, производят vld1.8 без спецификаторов выравнивания в данном конкретном случае.)

Обратите внимание, что это проблема только для 32-битного кронштейна; в AArch64 нет указателя выравнивания для команды ld1. Но даже там выравнивание по-прежнему, очевидно, помогает, вы получите худшую производительность, если будете использовать его с несогласованными адресами.

2

Глядя на это с другой стороны, вот фактическое определение этого типа с точки зрения одного примера компилятор (Visual Studio 2015-х arm_neon.h):

typedef union __declspec(intrin_type) _ADVSIMD_ALIGN(8) __n128 
{ 
    unsigned __int64 n128_u64[2]; 
    unsigned __int32 n128_u32[4]; 
    unsigned __int16 n128_u16[8]; 
    unsigned __int8 n128_u8[16]; 
    __int64   n128_i64[2]; 
    __int32   n128_i32[4]; 
    __int16   n128_i16[8]; 
    __int8    n128_i8[16]; 
    float    n128_f32[4]; 

    struct 
    { 
     __n64 low64; 
     __n64 high64; 
    } DUMMYNEONSTRUCT; 

} __n128; 

... 

typedef __n128 int8x16_t; 

Так, на платформах Windows, по крайней мере, это потребует не меньше, чем выравнивание __int64 благодаря этому соединению, а также от AAPCS, что означает 8 байтов (и даже без не очень сложной догадки, что может означать _ADVSIMD_ALIGN(8) ...)


Это даже проще, чем это, хотя, потому что получается, сказала AAPCS делает на самом деле последнее слово в этом непосредственно, через его определение векторных типов в терминах контейнерных векторов (§4.1. 2):

содержание контейнерного вектора является непрозрачным для большей части стандартной процедуры вызова: только Defined аспекта его расположения является отображением между форматом памяти (путем фундаментальным типа хранится в памяти) и различные классы регистров на интерфейсе процедуры вызова.

Другими словами, на уровне ABI тип вектора представляет собой тип вектора, независимо от того, может или не может быть в нем, и оба 64-битные и 128-битные контейнерные векторы требуют выравнивания 8 байт, поскольку ABI так говорит (п. 4.1). Таким образом, вне зависимости от того, что могут выполнять базовые инструкции, реализация Microsoft даже не является чрезмерно строгой, как я изначально предполагал, она просто соответствует. Восемь - это номер, который вы выровняете, а число выравнивания должно быть равно восьми.

С другой стороны, аргумент vld1q_u8() - это uint8_t const *, чьи указательные данные не имеют требования к выравниванию, поэтому можно утверждать, что оно соответствует 8-байтовому выравниванию, которое может сильно потерпеть неудачу.

+0

Разве это немного ортогонально? Речь идет о том, какое выравнивание имеет int8x16_t при хранении где-то, но в большинстве случаев вы хотите, чтобы он оставался только в регистре NEON. Это не влияет на то, что вы можете загружать в него данные из любого указателя, указывающего на неглавный адрес, с помощью 'vld1q_u8()', который, я считаю, был тем, о чем спрашивал ОП. – mstorsjo

+1

@mstorsjo Другие ответы на вопросы _direct_ уже довольно хорошо. Я подумал, что стоит также уточнить, почему именно код запрашивается, является ошибочным, хотя кажется, что я оставил вывод полностью неявным - исправлено! – Notlikethat

+0

Notlikethat и mstorsjo - я думаю, что это управляющий документ из ARM: [VLDn и VSTn (единственная n-элементная структура на одну полосу)] (http://infocenter.arm.com/help/index.jsp?topic=/ com.arm.doc.dui0489f/CIHCADCI.html), [VLDn (единственная структура n-элементов для всех дорожек)] (http://infocenter.arm.com/help/index.jsp?topic=/com.arm. doc.dui0489f/CIHCADCI.html) и [VLDn и VSTn (несколько n-элементных структур)] (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489f/CIHCADCI.html). Я не мог снова найти их, когда хотел очень многое (и я не могу попросить ссылку на SO). – jww