Bug 56298 - wmmintrin.h aborts compilation on the machines without AES
Summary: wmmintrin.h aborts compilation on the machines without AES
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 4.7.2
: P3 major
Target Milestone: 4.9.0
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-02-12 12:57 UTC by Piotr Wyderski
Modified: 2021-08-15 05:37 UTC (History)
3 users (show)

See Also:
Host:
Target: x86_64-*-*, i?86-*-*
Build:
Known to work:
Known to fail:
Last reconfirmed: 2013-02-12 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Piotr Wyderski 2013-02-12 12:57:50 UTC
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.
Comment 1 Jakub Jelinek 2013-02-12 13:13:50 UTC
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.
Comment 2 Richard Biener 2013-02-12 13:16:47 UTC
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.
Comment 3 Piotr Wyderski 2013-02-12 13:22:04 UTC
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.
Comment 4 Piotr Wyderski 2013-02-12 13:30:37 UTC
@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.
Comment 5 Richard Biener 2013-02-12 13:49:41 UTC
Can you give me a testcase that I can compile?
Comment 6 Piotr Wyderski 2013-02-12 13:55:08 UTC
#include <wmmintrin.h>

__m128i f(__m128i x, __m128i y) {

    return _mm_aesenc_si128(x, y);
}
Comment 7 Jakub Jelinek 2013-02-12 14:08:05 UTC
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.
Comment 8 Richard Biener 2013-02-12 15:47:33 UTC
(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
Comment 9 Piotr Wyderski 2013-02-12 17:22:08 UTC
(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.
Comment 10 Piotr Wyderski 2013-02-15 11:41:07 UTC
(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").
Comment 11 Andrew Pinski 2021-08-15 05:37:51 UTC
Fixed in r0-124016.