I have a piece of C++ code which computes a hash function using the AES-NI extensions of the Sandy/IvyBridge x64 processors. It is automatically selected if the target platform supports the mentioned extensions via cpuid. To use e.g. _mm_aesenc_si128() one needs to include <wmmintrin.h>, which is also a full test case here. If the host does not implement AES-NI (detected via -march=native or it is not explicitly enabled by -maes), the compilation process fails abruptly due to the content of wmmintrin.h: #if !defined (__AES__) && !defined (__PCLMUL__) # error "AES/PCLMUL instructions not enabled" #else It is a very serious bug (so I decided to mark is as "major"), because the intrinsics available should not depend on command line settings -- it is the user who takes full responsibility for their correct use and availability checking, as it is in my case. Enabling -maes is not an option, because it would then allow the code generator to unconditionally emit the AES-NI instructions in places I don't control, which will result in SIGILL and a core dump. This bug probably applies to all recent GCC versions, including 4.7.2 and 4.6.3. On MSVC2010 the respective header contains no such compile-time checks, i.e. it is fully consistent with the intended behaviour described above: /* * wmmintrin.h * * Principal header file for Intel(R) AES and PCLMULQDQ intrinsics. */ #pragma once #ifndef __midl #ifndef _INCLUDED_WMM #define _INCLUDED_WMM #if defined(_M_CEE_PURE) #error ERROR: EMM intrinsics not supported in the pure mode! #else #include <nmmintrin.h> #if __cplusplus extern "C" { #endif /* * Performs 1 round of AES decryption of the first m128i using * the second m128i as a round key. */ extern __m128i _mm_aesdec_si128(__m128i v, __m128i rkey); etc.
It is not a bug, it is a way all the intrinsics headers are written. You can use #pragma GCC push_options #pragma GCC target ("aes") #include <wmmintrin.h> #pragma GCC pop_options and similar.
The intrinsics do _not_ work if the corresponding CPU ISA feature is not enabled on the command-line. That's a fact - whether that's good is another question. A CPU feature agnostic intrinsics implementation would need to use inline-asm. A workaround is to put the AES-NI using function into a separate translation unit that you compile with -maes. This bug also applies to the new multiversioning feature or the target attribute feature all of which do not allow intrinsic header uses without globally enabling CPU ISA features (well, for the target attribute you could switch the ISA features _off_ in all places you do not want to use them ... ick). Thus, confirmed. The situation isn't really desirable (but maybe not easily fixable, too). The Intel compiler intrinsic headers do not use inline functions but the intrinsic decls directly (as if they were builtins). GCC 4.1 simply effectively made the intrinsics headers empty (by wrapping them in #ifdef FEATURE), at least since GCC 4.3 we have the #errors.
I beg to disagree, Jakub. In that case all the intrinsics headers are written in a wrong way. At least if one takes MSVC as a reference (which behaves exactly as I expected). Could somebody check how does ICC implement them? Thank you for the workaround, BTW.
@Richard: I don't have ICC right now, so a follow-up question is: does ICC "enable" those built-in intrinsics conditionally (as does GCC) or not (as MSVC). I think that ICC is the golden standard in this respect, so the answer would ultimately indicate whether my report is valid (and the bug should be fixed some day) or not. Thanks.
Can you give me a testcase that I can compile?
#include <wmmintrin.h> __m128i f(__m128i x, __m128i y) { return _mm_aesenc_si128(x, y); }
Headers are one thing, but you certainly can't use AES builtins in code not compiled with -maes or functions not using __attribute__((target ("aes"))) or similar. That just can't work, the insns the intrinsics want to use just aren't enabled in those ISA selections, and in other cases (think say just SSE2 ISA code, using AVX intrinsics) neither are the registers, nor modes available, in addition to the instructions.
(In reply to comment #6) > #include <wmmintrin.h> > > __m128i f(__m128i x, __m128i y) { > > return _mm_aesenc_si128(x, y); > } Compiling that with icc -S t.c results in f: # parameter 1: %xmm0 # parameter 2: %xmm1 ..B1.1: # Preds ..B1.0 ..___tag_value_f.1: #3.33 aesenc %xmm1, %xmm0 #5.16 ret #5.16
(In reply to comment #8) > Compiling that with icc -S t.c results in > > f: > # parameter 1: %xmm0 > # parameter 2: %xmm1 > ..B1.1: # Preds ..B1.0 > ..___tag_value_f.1: #3.33 > aesenc %xmm1, %xmm0 #5.16 > ret #5.16 So it seems that ICC and MSVC are in one team here, while GCC and CLang/LLVM are in the other. And since I consider ICC the one to be followed, as the *mmintrin.h files are provided in GCC for exactly that purpose, the conclusion is that GCC behaves in a wrong way. Jakub: you are right, the __builtin_* functions are not expected to be available when the target does not support them. But intrinsics are not like that and ultimately it is the (wrong) choice of a GCC implementer to base the intrinsic functions' implementation on builtins. Inline assembly would not cause such a problem. Just to be clear: I am perfectly aware how hard would it be to reimplement the intrinsics from scratch, but there *is* a problem.
(In reply to comment #1) More related problems -- do they deserve their own bug reports? 1. The following workaround provided by Jakub doesen't solve the #error problem: > #pragma GCC push_options > #pragma GCC target ("aes") > #include <wmmintrin.h> > #pragma GCC pop_options It is reported by checking the __AES__ macro, which is not defined by #pragma GCC target ("aes"). Similarly with target("sse4.1") and __SSE_4_1__ form nmmintrin.h. 2. Additionally, the use of intrinsic _mm_aeskeygenassist_si128, implemented as shown below: _mm_aeskeygenassist_si128 (__m128i __X, const int __C) { return (__m128i) __builtin_ia32_aeskeygenassist128 ((__v2di)__X, __C); } aborts compilation (on 4.7.2) claiming that the __builtin_ia32_aeskeygenassist128 function explicitly requires "-maes" in the command line, despite the fact it is used within a block surrounded by #pragma GCC target ("aes").
Fixed in r0-124016.