我在c中有这么简单的代码:
#include <stdio.h>
void test() {}
int main()
{
if (2 < 3) {
int zz = 10;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当我看到这段代码的汇编输出时:
test():
pushq %rbp
movq %rsp, %rbp
nop
popq %rbp
ret
main:
pushq %rbp
movq %rsp, %rbp
movl $10, -4(%rbp) // space is created for zz on stack
movl $0, %eax
popq %rbp
ret
Run Code Online (Sandbox Code Playgroud)
我从这里 得到了程序集(默认选项) 我看不出条件检查的指令在哪里?
为什么32位C将所有函数参数直接推送到堆栈上,而64位C将前6个参数放入寄存器而其余的放在堆栈中?
所以32位堆栈看起来像:
...
arg2
arg1
return address
old %rbp
Run Code Online (Sandbox Code Playgroud)
虽然64位堆栈看起来像:
...
arg8
arg7
return address
old %rbp
arg6
arg5
arg4
arg3
arg2
arg1
Run Code Online (Sandbox Code Playgroud)
那么为什么64位C会这样做呢?将所有内容都推送到堆栈而不是将前6个参数放在寄存器中以便将它们移动到函数序言中的堆栈中是不是更容易?
如果未使用关键字指定变量volatile,则编译器可能会进行缓存。必须始终从内存访问该变量,否则直到其事务单元结束。我想知道的重点在于装配零件。
int main() {
/* volatile */ int lock = 999;
while (lock);
}
Run Code Online (Sandbox Code Playgroud)
在x86-64-clang-3.0.0编译器上,其汇编代码如下。
main: # @main
mov DWORD PTR [RSP - 4], 0
mov DWORD PTR [RSP - 8], 999
.LBB0_1: # =>This Inner Loop Header: Depth=1
cmp DWORD PTR [RSP - 8], 0
je .LBB0_3
jmp .LBB0_1
.LBB0_3:
mov EAX, DWORD PTR [RSP - 4]
ret
Run Code Online (Sandbox Code Playgroud)
当volatile关键字被注释时,结果如下。
main: # @main
mov DWORD PTR [RSP - 4], 0
mov DWORD …Run Code Online (Sandbox Code Playgroud) 我有以下 C 函数:
void proc(long a1, long *a1p,
int a2, int *a2p,
short a3, short *a3p,
char a4, char *a4p)
{
*a1p += a1;
*a2p += a2;
*a3p += a3;
*a4p += a4;
}
Run Code Online (Sandbox Code Playgroud)
使用Godbolt,我已将其转换为 x86_64 程序集(为简单起见,我使用该-Og标志来最小化优化)。它产生以下组件:
proc:
movq 16(%rsp), %rax
addq %rdi, (%rsi)
addl %edx, (%rcx)
addw %r8w, (%r9)
movl 8(%rsp), %edx
addb %dl, (%rax)
ret
Run Code Online (Sandbox Code Playgroud)
我对汇编的第一行感到困惑:movq 16(%rsp), %rax. 我知道%rax寄存器用于存储返回值。但是该proc过程没有返回值。所以我很好奇为什么在此处使用该寄存器,而不是 %r9其他一些不用于返回值的寄存器。
相对于其他指令,我也对这条指令的位置感到困惑。它首先出现,远在%rax任何需要其目标寄存器之前(实际上,直到最后一步才需要该寄存器)。它也出现在 之前addq %rdi, (%rsi) …
我想我发现 gcc 编译器处理函数的方式有问题。
我不知道这是一个错误还是我多年来忽略的东西从来没有分心。实际上,通过声明一个函数并定义后者具有返回值,编译器将在函数范围内分配的第一个变量的值存储在 EAX 寄存器中,然后依次将其存储在一个变量中。例子:
#include<stdio.h>
int add(int a, int b)
{
int c = a + b;
;there isn't return
}
int main(void)
{
int res = add(3, 2);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是输出:
5
Run Code Online (Sandbox Code Playgroud)
这是具有 intel 语法的 x86-64 程序集:
功能添加:
push rbp
mov rbp, rsp
mov DWORD PTR[rbp-0x14], edi ;store first
mov DWORD PTR[rbp-0x18], esi ;store second
mov edx, DWORD PTR[rbp-0x14]
mov eax, DWORD PTR[rbp-0x18]
add eax, esx
mov DWORD PTR[rbp-0x4], eax
nop
pop rbp
ret
Run Code Online (Sandbox Code Playgroud)
主要功能:
push …Run Code Online (Sandbox Code Playgroud) 测试代码:
#include <array>
int test(const std::array<int, 10> &arr) {
return arr[9];
}
Run Code Online (Sandbox Code Playgroud)
我要实现arr[0]像C风格数组一样高效,这意味着内联STL数组[]运算符函数。
我检查了生成汇编代码:
$ g++ --std=c++17 -c test.cpp && objdump -d -C test.o
test.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <test(std::array<int, 10ul> const&)>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: 48 89 7d f8 mov %rdi,0xfffffffffffffff8(%rbp)
c: 48 8b 45 f8 mov 0xfffffffffffffff8(%rbp),%rax
10: be 09 00 00 00 mov $0x9,%esi
15: 48 89 …Run Code Online (Sandbox Code Playgroud) 我编写了一个最小函数来测试是否可以调用/链接 C 和 x86_64 汇编代码。
这是我的main.c
#include <stdio.h>
extern int test(int);
int main(int argc, char* argv[])
{
int a = 10;
int b = test(a);
printf("b=%d\n", b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是我的test.asm
section .text
global test
test:
mov ebx,2
add eax,ebx
ret
Run Code Online (Sandbox Code Playgroud)
我使用这个脚本构建了一个可执行文件
#!/usr/bin/env bash
nasm -f elf64 test.asm -o test.o
gcc -c main.c -o main.o
gcc main.o test.o -o a.out
Run Code Online (Sandbox Code Playgroud)
我写的时候test.asm并没有任何真正的线索我在做什么。然后我离开并做了一些阅读,现在我不明白我的代码是如何工作的,因为我说服自己它不应该这样。
以下是我认为这不起作用的原因列表:
eaxand传递的ebx。我认为这是不对的。ret可能希望从某个地方获取返回地址。我相当确定我没有提供这个。我所编写的内容产生正确的输出完全是侥幸吗?
我对此完全陌生。虽然我顺便听说过一些 …
编辑 -我的构建系统有问题。我仍在弄清楚到底是什么,但是gcc产生了奇怪的结果(即使它是一个.cpp文件),但是一旦使用,g++它就会按预期工作。
对于我一直遇到的问题,这是一个非常减少的测试用例,其中使用数字包装器类(我认为应该内联)使我的程序慢10倍。
这与优化级别无关(使用-O0和尝试-O3)。
我在包装器类中缺少一些细节吗?
我有以下程序,其中定义了一个包装a double并提供+操作符的类:
#include <cstdio>
#include <cstdlib>
#define INLINE __attribute__((always_inline)) inline
struct alignas(8) WrappedDouble {
double value;
INLINE friend const WrappedDouble operator+(const WrappedDouble& left, const WrappedDouble& right) {
return {left.value + right.value};
};
};
#define doubleType WrappedDouble // either "double" or "WrappedDouble"
int main() {
int N = 100000000;
doubleType* arr = (doubleType*)malloc(sizeof(doubleType)*N);
for (int i = 1; i < N; …Run Code Online (Sandbox Code Playgroud) 我一直认为num * 0.5f和num / 2.0f是等价的,因为我认为编译器足够聪明,可以优化除法。所以今天我决定测试一下这个理论,但我发现的结果却难住了我。
给出以下示例代码:
float mul(float num) {
return num * 0.5f;
}
float div(float num) {
return num / 2.0f;
}
Run Code Online (Sandbox Code Playgroud)
x86-64 clang 和 gcc 都会生成以下汇编输出:
mul(float):
push rbp
mov rbp, rsp
movss DWORD PTR [rbp-4], xmm0
movss xmm1, DWORD PTR [rbp-4]
movss xmm0, DWORD PTR .LC0[rip]
mulss xmm0, xmm1
pop rbp
ret
div(float):
push rbp
mov rbp, rsp
movss DWORD PTR [rbp-4], xmm0
movss xmm0, DWORD PTR [rbp-4]
movss xmm1, DWORD PTR …Run Code Online (Sandbox Code Playgroud) assembly ×6
c ×5
gcc ×4
x86-64 ×4
c++ ×3
c++11 ×1
compilation ×1
intrinsics ×1
linux ×1
performance ×1
stl ×1
volatile ×1