Ошибка C2078 при инициализации uint32x4_t на ARM? - PullRequest
0 голосов
/ 03 января 2019

Я тестирую сборку ARM с использованием Visual Studio 2013. Я улавливаю ошибку компиляции при инициализации uint32x4_t.Ошибка: error C2078: too many initializers.

const uint32x4_t CTRS[3] = {
    {1,0,0,0}, {2,0,0,0}, {3,0,0,0}
};

Это приводит к:

cl.exe /nologo /W4 /wd4231 /wd4511 /wd4156 /D_MBCS /Zi /TP /GR /EHsc /DNDEBUG /D_
NDEBUG /Oi /Oy /O2 /MT /FI sdkddkver.h /FI winapifamily.h /DWINAPI_FAMILY=WINAPI_
FAMILY_PHONE_APP /c chacha_simd.cpp
chacha_simd.cpp
chacha_simd.cpp(306) : error C2078: too many initializers
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio 12.0
\VC\BIN\x86_ARM\cl.exe"' : return code '0x2'
Stop.

Я вижу, что это известная проблема из форумов MSDN "ошибкаC2078: слишком много инициализаторов "при использовании ARM NEON .Это было подтверждено, но никакого обходного пути не было предоставлено.

Я также попробовал этот ужас (заимствуя из стиля PowerPC):

const uint32x4_t CTRS[3] = {
    vld1q_u32({1,0,0,0}),
    vld1q_u32({2,0,0,0}),
    vld1q_u32({3,0,0,0})
};

Это привело к:

chacha_simd.cpp(309) : warning C4002: too many actual parameters for macro 'vld1
q_u32'
chacha_simd.cpp(309) : error C2143: syntax error : missing '}' before ')'
chacha_simd.cpp(309) : error C2664: 'const __n64 *__uint32ToN64_c(const uint32_t
 *)' : cannot convert argument 1 from 'initializer-list' to 'const uint32_t *'
        Reason: cannot convert from 'int' to 'const uint32_t *'
        Conversion from integral type to pointer type requires reinterpret_cast,
 C-style cast or function-style cast
chacha_simd.cpp(309) : error C2660: '__neon_Q1Adr' : function does not take 1 ar
guments
chacha_simd.cpp(310) : warning C4002: too many actual parameters for macro 'vld1
q_u32'
chacha_simd.cpp(310) : error C2143: syntax error : missing '}' before ')'
chacha_simd.cpp(310) : error C2664: 'const __n64 *__uint32ToN64_c(const uint32_t
 *)' : cannot convert argument 1 from 'initializer-list' to 'const uint32_t *'
        Reason: cannot convert from 'int' to 'const uint32_t *'
        Conversion from integral type to pointer type requires reinterpret_cast,
 C-style cast or function-style cast
chacha_simd.cpp(310) : error C2660: '__neon_Q1Adr' : function does not take 1 ar
guments
chacha_simd.cpp(310) : fatal error C1903: unable to recover from previous error(
s); stopping compilation

Согласнок некоторому исходному коду для arm_neon.h на GitHub , __neon_Q1Adr и vld1q_u32:

__n128 __neon_Q1Adr(unsigned int, const __n64*);

#define vld1q_u32(pcD) ( __neon_Q1Adr( 0xf4200a8f, __uint32ToN64_c(pcD)) )

Вещи только запутываются.Поиск по сайту "arm initialize" uint32x4_t "site: microsoft.com" и "arm initialize" uint32x4_t "site: msdn.com" возвращает 0 обращений.

Как инициализировать uint32x4_t с помощью компиляторов Microsoft?

Ответы [ 2 ]

0 голосов
/ 03 января 2019

Ответ Джейка переносится компиляцией, но (как и для встроенных функций x86) компиляторы глупы и фактически копируют массив во время выполнения, когда вы используете встроенную функцию в качестве статического инициализатора.(Либо внутри функции, либо один раз в конструкторе, подобном статическому инициализатору.) Было бы более эффективно писать код, который индексирует базовый массив скаляров, например vld1q_u32(&array[idx*4])


Заголовок winddk-8.1Вы связали arm_neon.h, довольно четко показывает typedef __n128 uint32x4_t; (так же, как другие ширины элемента для 128-битных векторов), и что базовый тип __n128 определяется как объединение с __int64[2]первый член.

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;

Если вы хотите написать код только для MSVC, который зависит от внутренних элементов заголовка, вы можете просто объединить пары 32-разрядных целых чисел в 64-разрядные целые числа. Дляlittle-endian ARM, это означает, что второй 32-битный элемент должен быть high 32-битный из комбинированного 64-битного элемента.

#ifdef _MSC_VER
// MSVC only; will silently compile differently on others
static const uint32x4_t CTRS[3] = {
     // The .n128_u64 field is first in the definition of uint32x4_t
     {1 + (0ULL<<32), 0 + (0ULL<<32)},   // ARM is little-endian
     {2 + (0ULL<<32), 0 + (0ULL<<32)},
     {3 + (0ULL<<32), 0 + (0ULL<<32)},
};

Мы можем обернуть это с помощью CPPмакрос, чтобы сделать его переносимым между компиляторами

Я сделал один макрос для всего uint32x4_t, а не парный макрос, который вы также можете использовать для 64-битных векторов.Это делает фактические объявления менее беспорядочными скобками и именами макросов, потому что мы можем включить в этот макрос внешний {}.

#ifdef _MSC_VER
  // The .n128_u64 field is first.  Combine pairs of 32-bit integers in little-endian order.
#define INITu32x4(w,x,y,z) { ((w) + (unsigned long long(x) << 32)), ((y) + (unsigned long long(z) << 32)) }
#else
#define INITu32x4(w,x,y,z) { (w), (x), (y), (z) }
#endif

static const uint32x4_t CTRS[3] = {
 INITu32x4(1,0,0,0),
 INITu32x4(2,0,0,0),
 INITu32x4(3,0,0,0),
};

Компилируется правильно + эффективно в GCCи MSVC для нужных данных в разделе данных только для чтения (.rodata или .rdata), без инициализации во время выполнения. Из проводника компилятора Godbolt :

uint32x4_t access(int idx) {
  return CTRS[idx];
}

@ g++5.4 -O3 -Wall -mcpu=cortex-a53 -mfpu=neon -mfloat-abi=hard -std=gnu++11
access(int):
    movw    r3, #:lower16:.LANCHOR0
    movt    r3, #:upper16:.LANCHOR0    @ gcc chooses to construct the address with movw/movt
                                       @ instead of loading from a literal pool when optimizing for cortex-a53
    add     r0, r3, r0, lsl #4
    vld1.64 {d0-d1}, [r0:64]
    bx      lr

    .section        .rodata
    .align  3
    .set    .LANCHOR0,. + 0         @@ equivalent to .LANCHOR0: here. 
            @@ Reference point that could be used for other .rodata objects if needed.
    .type   CTRS, %object
    .size   CTRS, 48
CTRS:
    .word   1
    .word   0
    .word   0
    .word   0
    .word   2
    .word   0
     ...

И MSVC -Ox: я понятия не имею, почему директива MSVC DCQ по-прежнему нуждается в2 аргумента для создания одного 64-битного значения, точно такого же, как DCD, если вы создаете массив int.Кажется, это отличается от директивы / псевдоинструкции Кейла , где каждый разделенный запятыми аргумент равен 64-разрядному целому числу.

Но AFAICT, комментарии MSVCдобавлено точное представление числа для каждой строки.

 ;; ARM msvc19.14 -O2
    .rdata
|__n128 const * const CTRS| DCQ 0x1, 0x0           ;  = 0x0000000000000001 ; CTRS
        DCQ     0x0, 0x0                      ;  = 0x0000000000000000
        DCQ     0x2, 0x0                      ;  = 0x0000000000000002
        DCQ     0x0, 0x0                      ;  = 0x0000000000000000
        DCQ     0x3, 0x0                      ;  = 0x0000000000000003
        DCQ     0x0, 0x0                      ;  = 0x0000000000000000
        EXPORT  |__n128 access(int)|   ; access

.text$mn        SEGMENT

|__n128 access(int)| PROC                        ; access
        movw        r3,|__n128 const * const CTRS|
        movt        r3,|__n128 const * const CTRS|
        add         r3,r3,r0,lsl #4
        vldm        r3,{d0,d1}
|$M4|
        bx          lr

        ENDP  ; |__n128 access(int)|, access

В C (но не в C ++) MSVC допускает синтаксис назначенного инициализатора

static const uint32x4_t CTRS[3] = { [0].n128_u32 = {1, 0, 0, 0}, [1].n128_u32 = {2, 0, 0, 0}, [2].n128_u32 = {3, 0, 0, 0} };

uint32x4_t access(int idx) {
  return CTRS[idx];
}

.хорошо в режиме C MSVC, но не C ++.Вы можете использовать это для чуть более ориентированного на будущее определения INITu32x4, которое с шумом дает сбой, если что-то не так, и не сломается, если MS решит изменить порядок определения объединения.

У Godbolt есть режим языка C,Я обычно никогда не использую его (и просто использую -xc для g ++ / clang ++), потому что переключаться между ними неудобно, но я не знаю параметра командной строки, чтобы MSVC компилировался как C. В любом случае, thisна Годболте .

0 голосов
/ 03 января 2019

Ниже будет делать:

static const uint32_t array[] = {1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0};


const uint32x4_t CTRS[3] = {vld1q_u32(&array[0]), vld1q_u32(&array[4]), vld1q_u32(&array[8])};
...