让GAS从内联汇编生成指令吗?

时间:2017-04-19 15:13:06

标签: gcc inline-assembly gas arm64

我正在尝试组装使用ARM的CRC指令的文件。汇编程序产生错误 Error: selected processor does not support 'crc32b w1,w0,w0'

有运行时检查,所以我们对指令是安全的。该技术在i686和x86_64上运行良好。例如,我可以汇编使用英特尔CRC intrinsicsSHA Intrinsics而不使用-mcrc-msha的文件(并且在没有这些功能的计算机上)。

以下是测试用例:

$ 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 ("crc32b %w2, %w1, %w0" : "=r"(r) : "r"(crc), "r"((unsigned int)v));
  return r;
}
#else
  // Use the intrinsic
# define CRC32B(a,b) __crc32b(a,b)
#endif

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

结果如下:

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

将ASM代码放在源文件中并使用不同的选项进行编译是不可行的,因为CRC32B也将在C ++头文件中使用。

如何让GAS汇总指令?

GCC的配置和选项是我们尝试以这种方式做事的原因。用户不会阅读手册,因此他们无法将-march=armv8-a+crc+crypto -mtune=cortex-a53添加到CFLAGSCXXFLAGS

此外,发行版汇编成一个能力最差的&#34;机器,所以我们想要硬件加速例程。当库由Linaro这样的发行版提供时,两个代码路径(软件CRC和硬件加速CRC)都将可用。

该机器是LeMaker HiKey,它是ARMv8 / Aarch64。它有一个带有CRC和Crypto的A53处理器(CRC和Crypto在架构下是可选的):

$ cat /proc/cpuinfo
Processor       : AArch64 Processor rev 3 (aarch64)
processor       : 0
...
processor       : 7
Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer : 0x41
CPU architecture: AArch64

GCC缺少大多数通常默认存在的定义:

$ g++ -dM -E - </dev/null | sort | egrep -i '(arm|neon|aarch|asimd)'
#define __aarch64__ 1
#define __AARCH64_CMODEL_SMALL__ 1
#define __AARCH64EL__ 1

使用GCC&#39; -march=native不适用于ARM:

$ g++ -march=native -dM -E - </dev/null | sort | egrep -i '(arm|neon|aarch|asimd)'
cc1: error: unknown value ‘native’ for -march

和Clang:

$ clang++ -dM -E - </dev/null | sort | egrep -i '(arm|neon|aarch|asimd)'
#define __AARCH64EL__ 1
#define __ARM_64BIT_STATE 1
#define __ARM_ACLE 200
#define __ARM_ALIGN_MAX_STACK_PWR 4
#define __ARM_ARCH 8
#define __ARM_ARCH_ISA_A64 1
#define __ARM_ARCH_PROFILE 'A'
#define __ARM_FEATURE_CLZ 1
#define __ARM_FEATURE_DIV 1
#define __ARM_FEATURE_FMA 1
#define __ARM_FEATURE_UNALIGNED 1
#define __ARM_FP 0xe
#define __ARM_FP16_FORMAT_IEEE 1
#define __ARM_FP_FENV_ROUNDING 1
#define __ARM_NEON 1
#define __ARM_NEON_FP 0xe
#define __ARM_PCS_AAPCS64 1
#define __ARM_SIZEOF_MINIMAL_ENUM 4
#define __ARM_SIZEOF_WCHAR_T 4
#define __aarch64__ 1

GCC版本:

$ gcc -v
...
gcc version 4.9.2 (Debian/Linaro 4.9.2-10)

GAS版本:

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

1 个答案:

答案 0 :(得分:2)

这个答案来自王炯Binutils mailing list。它绕过了GAS的架构要求,并与GCC配合使用:

__inline unsigned int GCC_INLINE_ATTRIB
CRC32W(unsigned int crc, unsigned int val)
{
#if 1
    volatile unsigned int res;
    asm ("\n"
         "\t" ".set reg_x0, 0\n"
         "\t" ".set reg_x1, 1\n"
         "\t" ".set reg_x2, 2\n"
         "\t" ".set reg_x3, 3\n"
         "\t" ".set reg_x4, 4\n"
         "\t" ".set reg_x5, 5\n"
         "\t" ".set reg_x6, 6\n"
         "\t" ".set reg_x7, 7\n"
         "\t" "#crc32w %w0, %w1, %w2\n"
         "\t" ".inst 0x1ac04800 | (reg_%2 << 16) | (reg_%1 << 5) | (reg_%0)\n"
         : "=r"(res) : "r"(crc), "r"(val)
    );
    return res;
#else
    volatile unsigned int res;
    asm (".cpu generic+fp+simd+crc+crypto  \n"
         "crc32w %w0, %w1, %w2             \n"
         : "=r"(res) : "r"(crc), "r"(val));
    return res;
#endif
}

预处理器块注释的第二个是由Nick Clifton在Binutils mailing list上提出的。这个想法是GCC使用基于-march=XXX的ISA生成代码,所以如果我们增加通过汇编程序的能力并不重要。我们决定采用Wang的答案,因为我们不希望修改.cpu的潜在副作用。

使用GCC 4.8和Binutils 2.24进行验证:

$ g++ -O1 test.cxx -c

$ objdump --disassemble test.o

test.o:     file format elf64-littleaarch64

Disassembly of section .text:

0000000000000000 <main>:
   0:   12001c01        and     w1, w0, #0xff
   4:   1ac14800        crc32w  w0, w0, w1
   8:   d65f03c0        ret