错误:使用 gcc 32 位时,“asm”操作数具有不可能的约束
Posted
技术标签:
【中文标题】错误:使用 gcc 32 位时,“asm”操作数具有不可能的约束【英文标题】:error: ‘asm’ operand has impossible constraints when using gcc 32bit 【发布时间】:2016-05-28 00:52:53 【问题描述】:我正在尝试在 32 位 centos6 上编译 webrtc 并出现以下错误。但这在 64 位 centos 上运行良好。有人可以帮我吗?
row_gcc.cc:3574:4: error: ‘asm’ operand has impossible constraints
编译命令:
CXX obj/chromium/src/third_party/libyuv/source/libyuv.row_gcc.o
FAILED: obj/chromium/src/third_party/libyuv/source/libyuv.row_gcc.o
c++ -MMD -MF obj/chromium/src/third_party/libyuv/source/libyuv.row_gcc.o.d
-DV8_DEPRECATION_WARNINGS -D_FILE_OFFSET_BITS=64 -DCHROMIUM_BUILD
-DUI_COMPOSITOR_IMAGE_TRANSPORT -DUSE_AURA=1 -DUSE_PANGO=1 -DUSE_CAIRO=1
-DUSE_DEFAULT_RENDER_THEME=1 -DUSE_LIBJPEG_TURBO=1 -DUSE_X11=1
-DUSE_CLIPBOARD_AURAX11=1 -DENABLE_WEBRTC=1 -DENABLE_MEDIA_ROUTER=1
-DENABLE_PEPPER_CDMS -DENABLE_NOTIFICATIONS -DENABLE_TOPCHROME_MD=1
-DUSE_UDEV -DFIELDTRIAL_TESTING_ENABLED -DENABLE_TASK_MANAGER=1
-DENABLE_EXTENSIONS=1 -DENABLE_PDF=1 -DENABLE_PLUGINS=1
-DENABLE_SESSION_SERVICE=1 -DENABLE_THEMES=1 -DENABLE_PRINTING=1
-DENABLE_BASIC_PRINTING=1 -DENABLE_PRINT_PREVIEW=1 -DENABLE_SPELLCHECK=1
-DENABLE_CAPTIVE_PORTAL_DETECTION=1 -DENABLE_APP_LIST=1
-DENABLE_SETTINGS_APP=1 -DENABLE_SUPERVISED_USERS=1 -DENABLE_MDNS=1
-DENABLE_SERVICE_DISCOVERY=1 -DV8_USE_EXTERNAL_STARTUP_DATA
-DFULL_SAFE_BROWSING -DSAFE_BROWSING_CSD -DSAFE_BROWSING_DB_LOCAL
-DHAVE_JPEG -DUSE_LIBPCI=1 -DUSE_GLIB=1 -DUSE_NSS_CERTS=1
-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS
-DDYNAMIC_ANNOTATIONS_ENABLED=1 -DWTF_USE_DYNAMIC_ANNOTATIONS=1
-D_DEBUG -Igen
-I../../chromium/src/third_party/libyuv/include
-I../../chromium/src/third_party/libyuv
-I../../chromium/src/third_party/libjpeg_turbo -fstack-protector
--param=ssp-buffer-size=4 -pthread -fno-strict-aliasing -Wall -Wno-extra
-Wno-unused-parameter -Wno-missing-field-initializers -fvisibility=hidden
-pipe -fPIC
-B/home/test/webrtc-checkout/src/third_party/binutils/Linux_ia32/Release/bin
-Wno-unused-local-typedefs -msse2 -mfpmath=sse -mmmx -m32 -O0 -g
-funwind-tables -fno-exceptions -fno-rtti -fno-threadsafe-statics
-fvisibility-inlines-hidden
-std=gnu++11 -Wno-narrowing
-c ../../chromium/src/third_party/libyuv/source/row_gcc.cc
-o obj/chromium/src/third_party/libyuv/source/libyuv.row_gcc.o
../../chromium/src/third_party/libyuv/source/row_gcc.cc:
In function ‘void libyuv::BlendPlaneRow_SSSE3(const uint8*,
const uint8*, const uint8*, uint8*, int)’:
../../chromium/src/third_party/libyuv/source/row_gcc.cc:3574:4:
error: ‘asm’ operand has impossible constraints
gcc 版本:4.8.5
作为版本:GNU assembler (GNU Binutils) 2.26.20160125
third_party/binutils/Linux_ia32/Release/bin/as --version
GNU assembler (GNU Binutils) 2.26.20160125
Copyright (C) 2015 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or later.
This program has absolutely no warranty.
This assembler was configured for a target of `i686-pc-linux-gnu'.
chromium/src/third_party/libyuv/source/row_gcc.cc中的代码:
void BlendPlaneRow_SSSE3(const uint8* src0, const uint8* src1,
const uint8* alpha, uint8* dst, int width)
asm volatile (
"pcmpeqb %%xmm5,%%xmm5 \n"
"psllw $0x8,%%xmm5 \n"
"mov $0x80808080,%%eax \n"
"movd %%eax,%%xmm6 \n"
"pshufd $0x0,%%xmm6,%%xmm6 \n"
"mov $0x807f807f,%%eax \n"
"movd %%eax,%%xmm7 \n"
"pshufd $0x0,%%xmm7,%%xmm7 \n"
"sub %2,%0 \n"
"sub %2,%1 \n"
"sub %2,%3 \n"
// 8 pixel loop.
LABELALIGN
"1: \n"
"movq (%2),%%xmm0 \n"
"punpcklbw %%xmm0,%%xmm0 \n"
"pxor %%xmm5,%%xmm0 \n"
"movq (%0,%2,1),%%xmm1 \n"
"movq (%1,%2,1),%%xmm2 \n"
"punpcklbw %%xmm2,%%xmm1 \n"
"psubb %%xmm6,%%xmm1 \n"
"pmaddubsw %%xmm1,%%xmm0 \n"
"paddw %%xmm7,%%xmm0 \n"
"psrlw $0x8,%%xmm0 \n"
"packuswb %%xmm0,%%xmm0 \n"
"movq %%xmm0,(%3,%2,1) \n"
"lea 0x8(%2),%2 \n"
"sub $0x8,%4 \n"
"jg 1b \n"
: "+r"(src0), // %0
"+r"(src1), // %1
"+r"(alpha), // %2
"+r"(dst), // %3
"+r"(width) // %4
:: "memory", "cc", "eax", "xmm0", "xmm1", "xmm2", "xmm5", "xmm6", "xmm7"
);
【问题讨论】:
我没有立即发现这有什么问题。但是,您使用了很多寄存器。鉴于这适用于 x64,我想知道您是否快用完了?对于一个实验,如果你在 $0x80808080 移动之前push eax
,在 xmm7 移动之后将其弹出,然后将其从 clobber 中删除,会发生什么?这将获得 gcc 1 更多通用寄存器。无论如何都要尝试一下。
作为替代方案(或另外),您还可以将宽度从“+r”更改为“+m”。
David 是正确的,特别是 libyuv 假设有 5 个寄存器可用。通常这些通用寄存器可用EAX、EBX、ECX、EDX、ESI、EDI 和 EBP。 EAX 在clobber 列表中,因此无法使用。由于您没有省略堆栈帧指针,因此 EBP 不可用。因为它是用-fPIC
构建的,所以 EBX 不可用。本质上,在这个 32 位版本中,唯一可用的寄存器是 ECX、EDX、ESI、EDI。需要 5 个寄存器,4 个可用。可能会尝试使用-fomit-frame-pointer
甚至-O3
istead of -O0
重新编译它
大卫关于width
使用约束+rm
的建议没有错。我担心可能有其他代码可能会出现问题(寄存器用完),因此我尝试使用编译器选项来解决它(如果可能的话)。
@Michael Petch:我使用选项“-O2”进行编译,它可以工作。
【参考方案1】:
就像 David Wohlfred 所说的那样,问题在于您的寄存器用完了。有 8 个通用寄存器,其中一个是 ESP,堆栈指针,永远不能用于满足约束。由于您正在编译的选项,另外两个寄存器 EBP 和 EBX 也不能用于满足约束。由于您在没有优化的情况下进行编译,因此 EBP 寄存器为帧指针保留,并且因为您使用-fPIC
标志,EBX 寄存器被保留用于访问 GOT(全局对象表)。
因此留下五个可用于满足寄存器约束的寄存器,EAX、ECX、EDX、ESI 和 EDI。但是,您的 asm 语句破坏了 EAX,因此无法使用,只剩下四个寄存器。 asm 语句有五个具有寄存器约束的操作数,这意味着它需要五个单独的寄存器,但编译器只有四个可用。所以约束是不可能满足的。
一个简单的解决方案是通过在 asm 语句中删除 EAX 来释放寄存器。该寄存器用于将两个不同的常数值加载到两个不同的 XMM 寄存器中。相反,它可以直接从内存中加载常量。例如:
void BlendPlaneRow_SSSE3(const uint8* src0, const uint8* src1,
const uint8* alpha, uint8* dst, int width)
static unsigned const __attribute__((aligned(4))) xmm6[4] =
0x80808080, 0x80808080, 0x80808080, 0x80808080
;
static unsigned const __attribute__((aligned(4))) xmm7[4] =
0x807f807f, 0x807f807f, 0x807f807f, 0x807f807f
;
asm volatile (
"pcmpeqb %%xmm5,%%xmm5 \n"
"psllw $0x8,%%xmm5 \n"
"movaps %5,%%xmm6 \n"
"movaps %6,%%xmm7 \n"
"sub %2,%0 \n"
"sub %2,%1 \n"
"sub %2,%3 \n"
// 8 pixel loop.
LABELALIGN
"1: \n"
"movq (%2),%%xmm0 \n"
"punpcklbw %%xmm0,%%xmm0 \n"
"pxor %%xmm5,%%xmm0 \n"
"movq (%0,%2,1),%%xmm1 \n"
"movq (%1,%2,1),%%xmm2 \n"
"punpcklbw %%xmm2,%%xmm1 \n"
"psubb %%xmm6,%%xmm1 \n"
"pmaddubsw %%xmm1,%%xmm0 \n"
"paddw %%xmm7,%%xmm0 \n"
"psrlw $0x8,%%xmm0 \n"
"packuswb %%xmm0,%%xmm0 \n"
"movq %%xmm0,(%3,%2,1) \n"
"lea 0x8(%2),%2 \n"
"sub $0x8,%4 \n"
"jg 1b \n"
: "+r"(src0), // %0
"+r"(src1), // %1
"+r"(alpha), // %2
"+r"(dst), // %3
"+r"(width) // %4
: "m" (xmm6),
"m" (xmm7)
: "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm5", "xmm6", "xmm7"
);
更好的解决方案是重写它以使用内在函数并让编译器完成所有寄存器分配。
【讨论】:
或者只是要求常量在 xmm regs 中。您可以使用带有标量整数值的"x"
约束,编译器将为您提供movd
或movq
。
@PeterCordes 不知道,虽然我认为从内存中加载常量会比它替换的三个指令更快。
可能很好,是的。缓存未命中的风险通过 uop-cache 占用空间的减少来平衡。但是,0x80808080
可以是 generated on the fly with 2 more instructions: SSSE3 pabsb %xmm5, %xmm6
(0x010101...),然后左移 xmm6 以将这些单个设置位置于高位。
其实0x8080...
也可以从0xFF00...生成movdqa %xmm5, %xmm6
/packsswb %xmm6,%xmm6
。 (0xFF00 饱和到 0x80)。在循环外保存一条 ALU 指令(假设 mov 消除),可能还有 2 个字节的代码,但会减少 ILP。以上是关于错误:使用 gcc 32 位时,“asm”操作数具有不可能的约束的主要内容,如果未能解决你的问题,请参考以下文章
GCC 内联汇编错误:变量 '%al' 的 asm 说明符与 asm clobber 列表冲突
扩展 GCC asm 中多个输入和输出操作数的正确用法是啥?
gcc编译,出现错误:expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ........