我很难用内联汇编来击败我的编译器.
什么是一个好的,非人为的函数示例,编译器很难制作真正,快速和简单的函数?但是使用内联汇编制作相对简单.
我阅读了FastMM4的源代码,并注意到这个有趣的功能
function GetThreadID: Cardinal;
{$ifdef 32Bit}
asm
mov eax, FS:[$24]
end;
{$else}
begin
Result := GetCurrentThreadID;
end;
{$endif}
Run Code Online (Sandbox Code Playgroud)
我已经测试了它,它有效,所以我的问题是任何解释为什么它有效?
我正在编写C++代码来查找内存中非0xFF的第一个字节.为了利用bitscanforward,我编写了一个我非常喜欢的内联汇编代码.但是对于"可读性"以及未来的校对(即SIMD矢量化),我想我会给g ++优化器一个机会.g ++没有矢量化,但它确实得到了我所做的几乎相同的非SIMD解决方案.但由于某种原因,它的版本运行速度慢得多,速度慢260000倍(即我必须循环我的版本260,000x才能达到相同的执行时间).我除了一些差异,但不是那么多!有人可以指出它为什么会这样吗?我只是想知道在未来的内联汇编代码中出错.
C++的起点如下,(就计数准确性而言,此代码中存在一个错误,但我已将其简化为此速度测试):
uint64_t count3 (const void *data, uint64_t const &nBytes) {
uint64_t count = 0;
uint64_t block;
do {
block = *(uint64_t*)(data+count);
if ( block != (uint64_t)-1 ) {
/* count += __builtin_ctz(~block); ignore this for speed test*/
goto done;
};
count += sizeof(block);
} while ( count < nBytes );
done:
return (count>nBytes ? nBytes : count);
}
Run Code Online (Sandbox Code Playgroud)
汇编代码g ++提出的是:
_Z6count3PKvRKm:
.LFB33:
.cfi_startproc
mov rdx, QWORD PTR [rsi]
xor eax, eax
jmp .L19
.p2align 4,,10
.p2align 3 …Run Code Online (Sandbox Code Playgroud) GCC工具链默认使用AT&T汇编语法,但可通过该.intel_syntax指令获得对Intel语法的支持.
此外,AT和T以及英特尔语法都有a prefix和a noprefix版本,不同之处在于它们是否需要使用%sigil 为寄存器名称添加前缀.
根据存在的指令,地址常量的格式会发生变化.
我们考虑以下C代码
*(int *)0xdeadbeef = 0x1234;
Run Code Online (Sandbox Code Playgroud)
使用objdump -d,我们发现它被编译为以下汇编程序指令
movl $0x1234,0xdeadbeef
Run Code Online (Sandbox Code Playgroud)
由于没有涉及到寄存器,这对于正确的语法.att_syntax prefix和.att_syntax noprefix,即.嵌入在C代码中,它们看起来像这样
__asm__(".att_syntax prefix");
__asm__("movl $0x1234,0xdeadbeef");
__asm__(".att_syntax noprefix");
__asm__("movl $0x1234,0xdeadbeef");
Run Code Online (Sandbox Code Playgroud)
您可以选择用括号括起地址常量,即.
__asm__("movl $0x1234,(0xdeadbeef)");
Run Code Online (Sandbox Code Playgroud)
也会奏效.
将sigil添加到普通地址常量时,代码将无法复制
__asm__("movl $0x1234,$0xdeadbeef"); // won't compile
Run Code Online (Sandbox Code Playgroud)
当用paranthesis围绕这个表达式时,编译器将发出错误的代码而不发出警告,即
__asm__("movl $0x1234,($0xdeadbeef)"); // doesn't warn, but doesn't work!
Run Code Online (Sandbox Code Playgroud)
这将错误地发出指令
movl $0x1234,0x0
Run Code Online (Sandbox Code Playgroud)
在Intel模式下,PTR如果可能存在歧义,则地址常量必须以段寄存器为前缀,以及操作数大小和标志.在我的机器上(采用Windows XP和当前MinGW和Cygwin GCC版本的英特尔双核笔记本电脑),ds默认使用该寄存器.
常量周围的方括号是可选的.如果省略了段寄存器,但是括号存在,也可以正确识别地址常量.但是,忽略寄存器会在我的系统上发出警告.
在prefix模式中,段寄存器必须以前缀为前缀%,但仅使用括号仍然有效.这些是生成正确指令的不同方法:
__asm__(".intel_syntax noprefix");
__asm__("mov DWORD PTR ds:0xdeadbeef,0x1234");
__asm__("mov …Run Code Online (Sandbox Code Playgroud) 当我尝试编译此代码时:
#include <stdio.h>
main(int argc, char *argv[]) {
double y = 0;
__asm__ ("fldl $150;"
"fsqrt;"
"fstl %0;" : : "g" (y) );
printf("%f\n", y);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我收到此错误:
sqrt.c: Assembler messages:
sqrt.c:6: Error: suffix or operands invalid for `fld'
Run Code Online (Sandbox Code Playgroud)
为什么这不起作用?为什么我不能将数字"150"推入堆栈进行浮点运算?
我正在尝试为ia32编写GCC内联asm for CMPXCHG8B.不,我不能用__sync_bool_compare_and_swap.它必须使用和不使用-fPIC.
到目前为止,我所做的最好(编辑:毕竟不起作用,详见下面我自己的答案)
register int32 ebx_val asm("ebx")= set & 0xFFFFFFFF;
asm ("lock; cmpxchg8b %0;"
"setz %1;"
: "+m" (*a), "=q" (ret), "+A" (*cmp)
: "r" (ebx_val), "c" ((int32)(set >> 32))
: "flags")
Run Code Online (Sandbox Code Playgroud)
但是我不确定这实际上是否正确.
"b" ((int32)(set & 0xFFFFFFFF))由于PIC,我不能为ebx_val 做,但register asm("ebx")编译器接受了显然变量.
BONUS:ret变量用于分支,因此代码最终看起来像这样:
cmpxchg8b [edi];
setz cl;
cmp cl, 0;
je foo;
Run Code Online (Sandbox Code Playgroud)
任何想法如何描述输出操作数,使它成为:
cmpxchg8b [edi]
jz foo
Run Code Online (Sandbox Code Playgroud)
?
谢谢.
我想将C的outb函数移植到D.
static __inline void outb (unsigned char value, unsigned short int port)
{
__asm__ __volatile__ ("outb %b0,%w1"
:
:
"a" (value),
"Nd" (port));
}
Run Code Online (Sandbox Code Playgroud)
这是D版.
extern(C)
{
void outb (ubyte value, ushort port)
{
// I couldn't figure out this part
}
}
Run Code Online (Sandbox Code Playgroud)
这些是关于该主题的一些链接.
D内联汇编程序
GCC-内联汇编-HOWTO
http://ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
但我不懂汇编语言,所以我需要一些帮助.任何帮助,将不胜感激.谢谢.
我不明白这是怎么回事.
GCC内联汇编程序很难做到,但是对于标记符号信息非常具体,因此编译器知道你正在做什么.
Microsoft Visual C++的inline assember非常容易使用(它似乎总是Just Work),但我不知道它对你的代码有什么样的保证或假设.
VC++是否试图"自动检测"哪些寄存器被破坏?它如何知道如何更改寄存器和堆栈指针?它做出任何假设吗?如果是这样,你如何解决这些假设?
这不是一个微不足道的问题.
注意:我不需要意见或建议使用纯asm.我实际上需要完成我正在谈论的内容:在将结果分配给short int时,在没有此符号的情况下获取内联asm /零扩展optcode.
我正在处理一个滥用16位短路的库,我正在优化它.我需要使用内联asm添加一些优化函数.问题是在很多地方将函数的结果赋给short int.也就是说,编译器生成第u个或第s个arm操作码.
我的目标是避免这个问题,并确保不会生成这个无用的操作码.首先,我需要定义我的优化函数来返回short int.这样,如果将其分配给int或short int,则不会有额外的操作码来转换结果.
问题是我不知道如何跳过编译器在我自己的函数中生成的int-> short转换.
愚蠢的演员:*(short*)(void*)&value不起作用.编译器要么开始更多地解决堆栈制作问题,要么仍然使用相同的sxth来对结果进行签名扩展.
我为多个编译器编译,我能够为arm的armcc编译器解决它,但我不能用GCC完成它(我用4.4.3或4.6.3编译).使用armcc我在内联asm语句中使用短类型.在gcc中,即使我使用短编译器仍因某种原因认为需要符号扩展.
这是一个简单的代码片段,我无法与GCC合作,有关如何使其工作的任何建议?对于这个简单的例子,我将使用clz指令:
示例文件test.c文件:
static __inline short CLZ(int n)
{
short ret;
#ifdef __GNUC__
__asm__("clz %0, %1" : "=r"(ret) : "r"(n));
#else
__asm { clz ret, n; }
#endif
return ret;
}
//test function
short test_clz(int n)
{
return CLZ(n);
}
Run Code Online (Sandbox Code Playgroud)
这是我用armcc -c -O3得到的预期结果:
test_clz:
CLZ r0,r0
BX lr
Run Code Online (Sandbox Code Playgroud)
这是GCC -c -O3给我的不可接受的结果:
test_clz:
clz r0, r0
sxth r0, r0
bx lr
Run Code Online (Sandbox Code Playgroud)
另请注意,如果使用内部变量int ret;而不是 …
我的问题有些奇怪,但我会尽力解释.
看看linux内核的语言,我得到了C和汇编,即使我读了一篇文章说[引用] Unix的第二次迭代完全用C语言编写[/ quote]
我认为这是误导性的,但当我说内核有汇编代码时,我得到了两个问题
如果Linux内核真的完全用C语言编写,那么它是如何获得编译所需的GCC的呢?
我做了一个完整的find / -name *.s
,只是在/ usr/src/linux-headers- `uname -r/中的某处有一个汇编文件(asm-offset.s)
不知怎的,我不认为这有助于GCC的工作,那么linux如何在没有组装的情况下工作,或者如果它使用汇编它在哪里以及它如何在依赖于arch时稳定.
提前致谢
inline-assembly ×10
assembly ×6
c ×5
gcc ×4
x86 ×3
c++ ×2
arm ×1
d ×1
delphi ×1
fastmm ×1
kernel ×1
linux ×1
performance ×1
visual-c++ ×1
x87 ×1