文章目录
前言
引言
由于开发AT芯片进行脉冲计数时发现使用参数为结构体参数时会导致脉冲计数减少,查看汇编代码发现该编译器O0汇编代码访问结构体数据达到19个机器周期。使用O2优化时还是需要10个机器周期左右,因此对C函数调用、参数访问产生疑问。因此使用gcc、MinGW、MSVC三款编译器查看在x86平台上函数调用与结构体访问方式。
环境
-
VS2022
-
Microsoft Visual C++ 2022
-
VSCode 版本: 1.91.1 (user setup)
-
gcc version 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)
-
VMware® Workstation 17 Pro
-
gcc 11.4.0
-
vim 8.2.2121(Ubuntu)
-
Ubuntu20.04
说明
图片说明
本文章图片采取图床方式,图片可能失效(失效请联系[email protected])或者访问Hexo (hongdei.github.io)
x86汇编简易说明
指令 | 说明 | 示例(x86-64环境) |
---|---|---|
lea | 加载有效地址(Load Effective Address)。将操作数的有效地址(而非内存中的值)加载到目标寄存器中。 | lea rax, [rbx + 8*rcx] :将rbx 寄存器的值加上rcx 寄存器值的8倍,结果地址加载到rax 寄存器。 |
mov | 数据传送。将源操作数的值传送到目标操作数。根据操作数大小,可以是movb (字节)、movw (字)、movl (双字)、movq (四字)等。 | mov rax, 123 :将立即数123移动到rax 寄存器。mov dword ptr [rbx], eax :将eax 寄存器的值移动到rbx 指向的内存地址(32位)。 |
movq | 在x86-64架构中,用于移动64位(四字)数据。 | movq rax, rbx :将rbx 寄存器的值移动到rax 寄存器。 |
movw | 在x86架构中,用于移动16位(字)数据。在x86-64中较少直接使用,但可通过适当的前缀或操作数大小指示器使用。 | movw ax, bx (假设在16位或32位模式下):将bx 寄存器的低16位值移动到ax 寄存器。 |
call | 过程调用。将返回地址(紧随call 指令后的指令地址)推送到栈上,并跳转到指定的函数或地址执行。 | call myFunction :调用名为myFunction 的函数。 |
nop | 无操作(No Operation)。执行时什么也不做,常用于代码对齐、占位或调试目的。 | nop :不执行任何操作。 |
主要参考文件
结构体调用
在gcc、MinGW、MSVC三款编译器中结构体访问、函数访问均是以地址形式访问。
MSVC编译器
步骤
- 创建测试项目
- 设置断点
- 查看汇编代码
测试说明
本次测试定义函数fun1,结构体mian_t,并在主函数main中调用fun1,MinGW与GCC编译器仅编译器不同,测试代码完全一致。
`#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct
{
uint16_t a;
uint16_t b;
uint16_t c;
}main_t;
static void fun1(const main_t *t)
{
uint16_t a = t->a;
uint16_t b = t->b;
uint16_t c = t->c;
}
int main(int argc, char const *argv[])
{
main_t x;
x.a = 1;
x.b = 2;
x.c = 3;
fun1(&x);
printf(“%d %d %d\n”,x.a,x.b,x.c);
printf(“hello word\n”);
return 0;
}`
设置断点
VS可以在调试界面中查看程序汇编代码。在函数调用中设置断点(可以查看整个工程汇编代码)。
汇编代码
在调试选项中选择窗口->反汇编。
函数调用
函数调用主要是通过lea和call命令实现(直接访问函数地址)。
结构体访问
结构体访问主要通过mov访问,通过lea传入结构体地址。
MinGW编译器
MinGW使用VSCode编写,VScode使用MinGW编译器见xxx(文章还没写)。
步骤
-
创建测试项目
-
编译
-
查看汇编代码
测试项目
见MSVC编译器->测试说明
编译
使用不优化、汇编输出源信息。
gcc .\main.c -S -fverbose-asm -O0 -o main1
汇编代码
通过leaq和movq实现函数参数传递、call实现函数调用(直接访问函数地址)。
GCC编译器
本人采用VM虚拟机运行Ubuntun系统。
步骤
- 创建测试项目
- 编译
- 查看汇编代码
测试项目
见MSVC编译器->测试说明。
编译
见MinGW编译器->编译。
汇编代码
由于MinGW基于gcc编译器,因此在汇编代码上与MinGW基本一致。
结论
在gcc、MinGW、MSVC三款编译器中结构体访问、函数访问均是以地址形式访问。
标签:汇编,调用,代码,访问,编译器,MinGW,寄存器,结构 From: https://blog.csdn.net/hongdiefatal/article/details/140743620