Discussion:
Error: unknown pseudo-op: `.arch_extension'
Jeffrey Walton
2017-04-19 19:38:00 UTC
Permalink
I'm working on my HiKey. I'm trying to enable CRC extension on ARMv8
in the assembler regardless of the way GCC was built, and regardless
of the user's CFLAGS and CXXFLAGS. I'm encountering an assembler
error: "unknown pseudo-op: `.arch_extension'".

According to [1], I can use ".arch_extension" to enable it. According
to [2], ".arch_extension" is available in GCC 4.6 and GAS 2.21. My
version of Linaro provides GCC 4.9.2 and GAS 2.25.90. I can also
duplicate the issue on GCC113 (compiel farm), which provides GCC 4.8
and GAS 2.24.

The test program is below. Trying to compile it results in:

$ g++ test.cxx -c
/tmp/ccVZ6hiq.s: Assembler messages:
/tmp/ccVZ6hiq.s:24: Error: unknown pseudo-op: `.arch_extension'
/tmp/ccVZ6hiq.s:25: Error: selected processor does not support `crc32b w1,w0,w0'

Trying to compile without ".arch_extension" results in:

$ g++ test.cxx -c
/tmp/cci4wu6d.s: Assembler messages:
/tmp/cci4wu6d.s:24: Error: selected processor does not support `crc32b w1,w0,w0'

Its almost as if ".arch_extension" is not being properly recognized or consumed.

Any ideas what might be going wrong here?

**********

The program:

$ cat test.cxx
#include <arm_neon.h>

#define GCC_INLINE_ATTRIB __attribute__((__gnu_inline__,
__always_inline__, __artificial__))

#if defined(__GNUC__) && !defined(__ARM_FEATURE_CRC32)
__inline unsigned int GCC_INLINE_ATTRIB
CRC32B(unsigned int crc, unsigned char v)
{
unsigned int r;
asm (" \n"
".arch_extension crc \n"
"\t" "crc32b %w2, %w1, %w0 \n"
: "=r"(r) : "r"(crc), "r"((unsigned int)v));
return r;
}
#else
// just use the instrinsic
# define CRC32B(a,b) __crc32b(a,b)
#endif

int main(int argc, char* argv[])
{
return CRC32B(argc, argc);
}

**********

Versions...

$ gcc --version
gcc (Ubuntu/Linaro 4.8.4-2ubuntu1~14.04.3) 4.8.4

$ as -v
GNU assembler version 2.24 (aarch64-linux-gnu) using BFD version (GNU
Binutils for Ubuntu) 2.24

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.5 LTS
Release: 14.04
Codename: trusty

[1] https://sourceware.org/binutils/docs/as/AArch64-Directives.html#AArch64-Directives
[2] https://gcc.gnu.org/ml/gcc-help/2012-07/msg00180.html
Jim Wilson
2017-04-20 01:57:38 UTC
Permalink
Post by Jeffrey Walton
According to [1], I can use ".arch_extension" to enable it. According
to [2], ".arch_extension" is available in GCC 4.6 and GAS 2.21. My
version of Linaro provides GCC 4.9.2 and GAS 2.25.90. I can also
duplicate the issue on GCC113 (compiel farm), which provides GCC 4.8
and GAS 2.24.
ARM is a little ambiguous. It turns out that arch_extension was added
to the arm (32-bit) port in 2010, but wasn't added to the aarch64
(64-bit) port until 2014.
So you need binutils-2.26 in order to use arch_extension pseudo-op
with the aarch64 toolchain.

Meanwhile, you might try looking at the arm_neon.h header file for
your gcc version. Though it apperas in gcc-4.9.2 there is only a
predefined macro __ARM_FEATURE_CRYPTO that you can use, and nothing
for CRC. GCC 5.x adds a __ARM_FEATURE_CRC32 predefined macro that you
could use.

With gcc 6, the arm_neon.h file uses
#pragma GCC push_options
#pragma GCC target ("+nothing+crypto")
...
#pragma GCC pop_options
to enable crypto support. You can do something similar with crc, you
probably want to use "+onthing+crc" if you only want crc support
enalbed, or "+simd+crc" if you want both simd and crc support enabled
for instance.

The GCC aarch64 port does not use the arch64_extension support in binutils.

I think the linux kernel just puts functions that need crc support in
different files, so that those files can be compiled with crc support
enabled via -mcpu=generic+crc.

Jim
Jeffrey Walton
2017-04-20 06:35:17 UTC
Permalink
Post by Jim Wilson
Post by Jeffrey Walton
According to [1], I can use ".arch_extension" to enable it. According
to [2], ".arch_extension" is available in GCC 4.6 and GAS 2.21. My
version of Linaro provides GCC 4.9.2 and GAS 2.25.90. I can also
duplicate the issue on GCC113 (compiel farm), which provides GCC 4.8
and GAS 2.24.
ARM is a little ambiguous. It turns out that arch_extension was added
to the arm (32-bit) port in 2010, but wasn't added to the aarch64
(64-bit) port until 2014.
So you need binutils-2.26 in order to use arch_extension pseudo-op
with the aarch64 toolchain.
Meanwhile, you might try looking at the arm_neon.h header file for
your gcc version. Though it apperas in gcc-4.9.2 there is only a
predefined macro __ARM_FEATURE_CRYPTO that you can use, and nothing
for CRC. GCC 5.x adds a __ARM_FEATURE_CRC32 predefined macro that you
could use.
With gcc 6, the arm_neon.h file uses
#pragma GCC push_options
#pragma GCC target ("+nothing+crypto")
...
#pragma GCC pop_options
to enable crypto support. You can do something similar with crc, you
probably want to use "+onthing+crc" if you only want crc support
enalbed, or "+simd+crc" if you want both simd and crc support enabled
for instance.
The GCC aarch64 port does not use the arch64_extension support in binutils.
I think the linux kernel just puts functions that need crc support in
different files, so that those files can be compiled with crc support
enabled via -mcpu=generic+crc.
Ah, thanks Jim. That would have taken me a long time to discover.

I've had some success with something similar to the kernel method. The
program below works with GCC 4.8, 4.9 and later.

The thing I am not sure about is saving and restoring the cpu. The AS
manual states "Specifying .cpu clears any previously selected
architecture extensions". But arch_extensions does not seem to work,
so I guess its a moot point. Also, the manual only discusses '.set' in
the context of MIPS. The program below does not produce a warning or
error. I am not sure if its doing what's expected.

I'm trying to avoid adding an additional source file for each source
file that uses NEON, CRC or CRYPTO. We are a C++ project, and it means
moving functions out of headers and into source files. Additionally,
we have 175 source files (plus headers), and the strategy could double
the counts.

Our choices are kind of crummy at this point...

Jeff

$ cat test.cxx
#include <arm_neon.h>
#include <arm_acle.h>

#define GCC_INLINE_ATTRIB __attribute__((__gnu_inline__,
__always_inline__, __artificial__))

#if defined(__GNUC__) && !defined(__ARM_FEATURE_CRC32)
__inline unsigned int GCC_INLINE_ATTRIB
CRC32B(unsigned int crc, unsigned char v)
{
unsigned int r;
asm (".set push, .cpu \n"
".cpu generic+crc \n"
"crc32w %w2, %w1, %w0 \n"
".set pop, .cpu \n"
: "=r"(r) : "r"(crc), "r"((unsigned int)v));
return r;
}
#else
// just use the intrinsic
# define CRC32B(a,b) __crc32b(a,b)
#endif

int main(int argc, char* argv[])
{
return CRC32B(argc, argc);
}
Jim Wilson
2017-04-20 07:50:40 UTC
Permalink
Post by Jeffrey Walton
The thing I am not sure about is saving and restoring the cpu. The AS
manual states "Specifying .cpu clears any previously selected
architecture extensions". But arch_extensions does not seem to work,
so I guess its a moot point. Also, the manual only discusses '.set' in
the context of MIPS. The program below does not produce a warning or
error. I am not sure if its doing what's expected.
For a non-mips target, .set is the same as .equ and =, it sets a
symbol value to an expression. Your code using .set push and .set pop
isn't doing anything useful.

I don't see any mechanism in the assembler to save/restore a cpu
setting. There is one in gcc, but not until gcc 6.

I don't see any easy solution for you at the moment, you need a
binutils/gcc upgrade, or you need to put crc code in separate files.
Or you could force all files to be compiled with -mcpu=generic, and
then you can use .cpu to add/remove crc support as necessary.

Jim
Jeffrey Walton
2017-04-20 14:15:44 UTC
Permalink
Post by Jim Wilson
I don't see any easy solution for you at the moment, you need a
binutils/gcc upgrade, or you need to put crc code in separate files.
Or you could force all files to be compiled with -mcpu=generic, and
then you can use .cpu to add/remove crc support as necessary.
Thanks Jim. This came from Jiong Wang on the Binutils mailing list
(https://sourceware.org/ml/binutils/2017-04/msg00171.html):

__inline unsigned int GCC_INLINE_ATTRIB
CRC32B(unsigned int crc, unsigned char v)
{
unsigned int r;
asm ("\t.set raw_x0, 0\n"
"\t.set raw_x1, 1\n"
"\t.set raw_x2, 2\n"
"\t.set raw_x3, 3\n"
"\t.set raw_x4, 4\n"
"\t.set raw_x5, 5\n"
"\t.set raw_x6, 6\n"
"\t.set raw_x7, 7\n"
"\t#crc32w %w2, %w1, %w0\n"
"\t.inst\t0x1ac04800 | (raw_%2) | (raw_%1 << 5) | (raw_%0 << 16)\n"
: "=r"(r) : "r"(crc), "r"((unsigned int)v)
);
return r;
}

Thanks for the help (and thanks to the Binutil folks). I would not
have gotten this far on my own.

Jeff

Loading...