Tag: x86

在ASM中,c编译器如何处理函数的结构返回值

在谈到C函数的返回值时,返回值存储在EAX寄存器中。 假设我们正在谈论32位寄存器,欢迎整数,但是当我们返回这些类型时会发生什么: long long , long double ,一个大于32位的struct / union 。

移位n位的__m128i

我有一个__m128i变量,我需要将其n位的128位值移位,即_mm_srli_si128和_mm_slli_si128工作,但是在位而不是字节上。 这样做最有效的方法是什么?

GCC INLINE ASSEMBLY不会让我覆盖$ esp

我正在编写代码暂时使用我自己的堆栈进行实验。 当我使用文字内联汇编时,这很有效。 我将变量位置硬编码为ebp的偏移量。 但是,我希望我的代码可以在不使用硬编码内存地址的情况下工作,所以我一直在研究GCC的扩展内联汇编。 我所拥有的是以下内容: volatile intptr_t new_stack_ptr = (intptr_t) MY_STACK_POINTER; volatile intptr_t old_stack_ptr = 0; asm __volatile__(“movl %%esp, %0\n\t” “movl %1, %%esp” : “=r”(old_stack_ptr) /* output */ : “r”(new_stack_ptr) /* input */ ); 这一点是首先将堆栈指针保存到变量old_stack_ptr中。 接下来,堆栈指针(%esp)将被我在new_stack_ptr中保存的地址覆盖。 尽管如此,我发现GCC将%esp保存到old_stack_ptr中,但是没有用new_stack_ptr替换%esp。 经过深入检查,我发现它实际上扩展了我的程序集并添加了它自己的指令,如下所示: mov -0x14(%ebp),%eax mov %esp,%eax mov %eax,%esp mov %eax,-0x18(%ebp) 我认为GCC正试图保留%esp,因为我没有将它明确声明为“输出”操作数……我可能完全错了… 我真的很想使用扩展的内联汇编来实现这一点,因为如果没有,似乎我必须将%ebp的位置偏移“硬编码”到程序集中,我宁愿使用像这样的变量名。特别是因为这段代码需要在几个不同的系统上运行,这些系统似乎都以不同的方式抵消了我的变量,因此使用扩展的内联汇编可以让我明确地说出变量位置……但是我不明白它为什么会这样做额外的东西,不要让我像以前一样覆盖堆栈指针,自从我开始使用扩展程序集以来,它一直在这样做。 我感谢任何帮助!

GCC优化对比特操作的有效性

以下是在x86-64上设置C中的单个位的两种方法: inline void SetBitC(long *array, int bit) { //Pure C version *array |= 1<<bit; } inline void SetBitASM(long *array, int bit) { // Using inline x86 assembly asm("bts %1,%0" : "+r" (*array) : "g" (bit)); } 使用带有-O3 -march=core2选项的GCC 4.3,当使用常量bit时,C版本需要大约90%的时间 。 (两个版本编译为完全相同的汇编代码,除了C版本使用or [1<<num],%rax指令而不是bts [num],%rax指令) 与变量bit ,C版本表现更好,但仍然明显慢于内联汇编。 重置,切换和检查位具有类似的结果。 为什么GCC对这种常见操作的优化程度如此之差? 我是否在使用C版本做错了什么? 编辑:对不起,等待很长时间,这是我用来进行基准测试的代码。 它实际上是一个简单的编程问题… int main() { // Get […]

什么在堆栈内?

如果我运行程序,就像 #include int main(int argc, char *argv[], char *env[]) { printf(“My references are at %p, %p, %p\n”, &argc, &argv, &env); } 我们可以看到这些区域实际上在堆栈中。 但还有什么呢? 如果我们在Linux 3.5.3中运行所有值的循环(例如,直到segfault),我们可以看到一些奇怪的数字,以及由一堆零分隔的两个区域的种类,可能是为了防止覆盖环境变量偶然。 无论如何,在第一个区域必须有很多数字,例如每个函数调用的所有帧。 我们怎样才能区分每个帧的结尾,参数在哪里,如果编译器添加了一个金丝雀,返回地址,CPU状态等等?

C / C ++中的简单“Hello World”内联汇编语言程序

我用devcpp和borland c编译器…. asm { mov ax,4 // (I/O Func.) mov bx,1 // (Output func) mov cx,&name // (address of the string) mov dx,6 // (length of the string) int 0x21 // system call } 在上面的代码片段中,我想在汇编语言的帮助下打印一个字符串…但是如何将字符串的地址放在寄存器cx …. 代码中有什么问题???

最佳SSE无符号8位比较

我试图找到使用SSE执行8位无符号比较的最多方法(直到SSE 4.2)。 我正在研究的最常见的情况是比较> 0U,例如 _mm_cmpgt_epu8(v, _mm_setzero_si128()) // #1 (这当然也可以被认为是非零的简单测试。) 但我对更一般的情况也有点兴趣,例如 _mm_cmpgt_epu8(v1, v2) // #2 第一种情况可以使用各种不同的方法用2条指令实现,例如与0比较然后反转结果。 第二种情况通常需要3条指令,例如从两个操作数中减去128并执行带符号的比较。 (有关各种3种指令解决方案,请参阅此问题 。) 理想情况下,我正在寻找#1的单指令解决方案,以及#2的双指令解决方案。 如果这些都不可能,那么我也对在现代英特尔CPU(Sandy Bridge,Ivy Bridge,Haswell)上哪种各样的可能的2或3指令实现最有效的想法感兴趣。 到目前为止,案例#2的最佳实现: 比较等于无符号最大值和反转结果: #define _mm_cmpgt_epu8(v0, v1) \ _mm_andnot_si128(_mm_cmpeq_epi8(_mm_max_epu8(v0, v1), v1), \ _mm_set1_epi8(-1)) 两个算术指令+一个按位= 1.33吞吐量。 反转两个参数的符号位(==减去128)并使用带符号的比较: #define _mm_cmpgt_epu8(v0, v1) \ _mm_cmpgt_epi8(_mm_xor_si128(v0, _mm_set1_epi8(-128)), \ _mm_xor_si128(v1, _mm_set1_epi8(-128))) 一个算术指令+两个按位= 1.16吞吐量。 案例#1的最佳实现,源自上面的案例#2实现: 1。 #define _mm_cmpgtz_epu8(v0) \ _mm_andnot_si128(_mm_cmpeq_epi8(v0, _mm_set1_epi8(0)), \ […]

快速逐字节替换if

我有一个函数可以将二进制数据从一个区域复制到另一个区域,但前提是这些字节与特定值不同。 这是一个代码示例: void copy_if(char* src, char* dest, size_t size, char ignore) { for (size_t i = 0; i < size; ++i) { if (src[i] != ignore) dest[i] = src[i]; } } 问题是这对我目前的需求来说太慢了。 有没有办法以更快的方式获得相同的结果? 更新:基于答案,我尝试了两个新的实现: void copy_if_vectorized(const uint8_t* src, uint8_t* dest, size_t size, char ignore) { for (size_t i = 0; i < size; ++i) { […]

SIMD以下代码

如何在C中简化以下代码(当然使用SIMD内在函数)? 我无法理解SIMD内在函数,这会有很大帮助: int sum_naive( int n, int *a ) { int sum = 0; for( int i = 0; i < n; i++ ) sum += a[i]; return sum; }

避免gccfunction序幕开销?

我最近遇到了很多函数,其中gcc在x86上生成了非常糟糕的代码。 它们都符合以下模式: if (some_condition) { /* do something really simple and return */ } else { /* something complex that needs lots of registers */ } 将简单的情况看作是一个非常小的东西,以至于一半或更多的工作花在推动和弹出不会被修改的寄存器上。 如果我手动编写asm,我会在复杂的情况下保存并恢复已保存的跨调用寄存器,并且在简单的情况下完全避免触及堆栈指针。 有没有什么方法可以让gcc变得更聪明并且自己做到这一点? 最好使用命令行选项,而不是源代码中的丑陋黑客… 编辑:为了使它具体化,这里有一些非常接近我正在处理的一些函数: if (buf->pos end) { return *buf->pos++; } else { /* fill buffer */ } 另一个: if (!initialized) { /* complex initialization procedure */ } […]