我有以下 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) …
为了将 __m256i 存储到特定的 YMM 寄存器(比如 YMM10),我使用以下代码
__m256i addr;
//load value to addr
asm ("vmovdqa %0,%%ymm10\n\t"
:
: "x" (addr)
:);
Run Code Online (Sandbox Code Playgroud)
为了将 YMM10 中的值加载到变量,我使用以下代码
__m256i readbuff;
asm ("vmovdqa %%ymm10,%0\n\t"\
: "=x" (readbuff)\
:\
:);
Run Code Online (Sandbox Code Playgroud)
我在这里面临的问题是,在我用一个值加载 YMM10 之后,我只使用了加载了值的寄存器的一半。我的意思是只加载了 128 位,另一半全为零。
我做错了什么吗?我不确定要使用什么指令 - vmovdqa、vmovaps、vmovups。请就此给我建议。
我正在编写一个小程序,用 printf("\219") 打印一个多边形,看看我正在做的事情是否适合我的内核。但它需要调用很多函数,我不知道x86处理器是否可以接受那么多子例程,而且我在google中找不到结果。所以我的问题是它是否会接受这么多函数调用以及最大值是多少。(我的意思是这样的:-)
function a() {b();}
function b() {c();}
function c() {d();}
...
Run Code Online (Sandbox Code Playgroud)
我已经使用了 5 个这样的级别(你知道我的意思,对吧?)
我编写了一个最小函数来测试是否可以调用/链接 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可能希望从某个地方获取返回地址。我相当确定我没有提供这个。我所编写的内容产生正确的输出完全是侥幸吗?
我对此完全陌生。虽然我顺便听说过一些 …
下面是一些 C++ 伪代码作为示例:
bool importantFlag = false;
for (SomeObject obj : arr) {
if (obj.someBool) {
importantFlag = true;
}
obj.doSomethingUnrelated();
}
Run Code Online (Sandbox Code Playgroud)
显然,一旦 if 语句评估为 true 并运行内部代码,就没有理由再次执行检查,因为无论哪种方式结果都是相同的。编译器是否足够聪明,能够识别这一点,还是会在每次循环迭代中继续检查 if 语句,并可能再次将 importantFlag 冗余地分配为 true?如果循环迭代次数很大,并且无法跳出循环,则这可能会对性能产生显着影响。
我通常会忽略这些情况,只是将信心寄托在编译器上,但如果能确切地知道它如何处理这些情况,那就太好了。
作为一个新手,我正在遵循教程。一种是在 VS 2022 的内联汇编中将字符串中的所有字符大写:
int main()
{
char mystr[] = "Hello World:";
_asm
{
mov ecx, length mystr
my: cmp [mystr + ecx], 'a';
jl nocap;
cmp [mystr + ecx], 'z';
ja nocap;
sub [mystr + ecx], 32;
nocap:
loop my
}
std::cout << mystr;
Run Code Online (Sandbox Code Playgroud)
我的问题是:为什么这个程序集不需要节,例如 .data、.text 或 _start:示例中可能混合了 x86 asm 和 Linux asm。
让我们从一个简单的堆栈分配示例开始:
void f() {
int a, b;
...
}
Run Code Online (Sandbox Code Playgroud)
如果我理解正确的话。a那么和的地址b与栈基址(即寄存器 )有固定的偏移量ebp。如果我们以后需要它们,这就是编译器找到它们的方式。
但请考虑以下代码。
void f(int n) {
int a;
alloca(n);
int b;
...
}
Run Code Online (Sandbox Code Playgroud)
如果编译器不做任何优化,堆栈将为a->n->b. 现在 的偏移量b取决于n。那么编译器做了什么?
模仿alloca() 在内存级别如何工作?。我尝试了以下代码:
#include <stdio.h>
#include <alloca.h>
void foo(int n)
{
int a;
int *b = alloca(n * sizeof(int));
int c;
printf("&a=%p, b=%p, &c=%p\n", (void *)&a, (void *)b, (void *)&c);
}
int main()
{
foo(5);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出是&a=0x7fffbab59d68, b=0x7fffbab59d30, …
汇编:
.intel_syntax noprefix
.global Foo
Foo:
mov ax, 146
ret
Run Code Online (Sandbox Code Playgroud)
主要.c:
#include <stdio.h>
extern int Foo(void);
int main(int argc, char** args){
printf("Asm returned %d\n", Foo());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在我编译并链接:
(compiler name) -c asm.s -o asm.o
(compiler name) asm.o main.c -o main
./main
Run Code Online (Sandbox Code Playgroud)
我正在使用 Windows 和 LLVM 的 x64 windows 二进制文件。
GCC 打印 146(如预期),但 clang 生成的代码打印随机值,为什么?在 Windows 上使用 gdb 调试 clang 二进制文件显然存在一些问题,因此我无法提供任何 gdb 日志。GCC 二进制文件正在执行我期望的操作,但不是 Clang。
我试图找到从ac程序生成的汇编代码的含义.这是C中的程序:
int* a = &argc;
int b = 8;
a = &b;
Run Code Online (Sandbox Code Playgroud)
以下是使用说明生成的汇编代码.有一部分我不明白:
主要序言:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
Run Code Online (Sandbox Code Playgroud)
在%eax中加载argc的地址:
movl %ecx, %eax
Run Code Online (Sandbox Code Playgroud)
我得不到的部分:
movl 4(%eax), %edx
movl %edx, -28(%ebp)
Run Code Online (Sandbox Code Playgroud)
Stack-Smashing Protector代码(设置):
movl %gs:20, %ecx
movl %ecx, -12(%ebp)
xorl %ecx, %ecx
Run Code Online (Sandbox Code Playgroud)
在a和b中加载值(参见main.c):
movl %eax, -16(%ebp)
movl $8, -20(%ebp)
Run Code Online (Sandbox Code Playgroud)
修改a(a =&b)的值:
leal -20(%ebp), %eax
movl %eax, -16(%ebp)
Run Code Online (Sandbox Code Playgroud)
Stack-Smashing Protector代码(验证堆栈是否正常):
movl $0, %eax
movl -12(%ebp), %edx
xorl %gs:20, %edx …Run Code Online (Sandbox Code Playgroud) 通过一些研究,我意识到 ARM 和 RISC 几乎可以互换使用,x86 和 CISC 也是如此。我理解RISC和CISC是架构。我的理解是,架构(即 RISC 或 CISC)是一种指令集,进程必须能够执行这些指令才能成为这些架构之一。例如,RISC-V 有一个它可以执行的指令列表,CISC 有一个它可以执行的指令列表。要成为 RISC 或 CISC,处理器必须能够执行特定的指令列表之一。但是,我不明白ARM和RISC以及x86和CISC分别有什么区别。ARM和x86不也是架构吗?我经常读到“ARM 架构”或“x86 架构”。感谢您为我澄清这一点。