相关疑难解决方法(0)

为什么 %rax 寄存器在这个程序的程序集中使用 8 个参数?

我有以下 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) …

assembly x86-64 cpu-registers compiler-optimization

0
推荐指数
1
解决办法
384
查看次数

使用 gcc 内联汇编加载和存储 YMM 寄存器


我正在尝试使用 gcc 内联 asm 加载和存储 YMM 寄存器。我使用 vmovdqa 来做这件事。

为了将 __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。请就此给我建议。

c x86 gcc inline-assembly avx2

0
推荐指数
1
解决办法
143
查看次数

x86 处理器可以调用多少个子例程?

我正在编写一个小程序,用 printf("\219") 打印一个多边形,看看我正在做的事情是否适合我的内核。但它需要调用很多函数,我不知道x86处理器是否可以接受那么多子例程,而且我在google中找不到结果。所以我的问题是它是否会接受这么多函数调用以及最大值是多少。(我的意思是这样的:-)

function a() {b();}
function b() {c();}
function c() {d();}
...
Run Code Online (Sandbox Code Playgroud)

我已经使用了 5 个这样的级别(你知道我的意思,对吧?)

c++ stack-overflow x86 gcc callstack

0
推荐指数
1
解决办法
357
查看次数

x86_64 Linux 系统的 GCC 调用约定

我编写了一个最小函数来测试是否可以调用/链接 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并没有任何真正的线索我在做什么。然后我离开并做了一些阅读,现在我不明白我的代码是如何工作的,因为我说服自己它不应该这样。

以下是我认为这不起作用的原因列表:

  • 我不保存或恢复基指针(设置堆栈帧)。我实际上不明白为什么需要这样做,但我看过的每个例子都是这样做的。
  • Linux 系统上 gcc 编译器的调用约定应该是通过堆栈传递参数。这里我假设参数是使用eaxand传递的ebx。我认为这是不对的。
  • ret可能希望从某个地方获取返回地址。我相当确定我没有提供这个。
  • 甚至可能还有其他我不知道的原因。

我所编写的内容产生正确的输出完全是侥幸吗?

我对此完全陌生。虽然我顺便听说过一些 …

c linux gcc x86-64 calling-convention

0
推荐指数
1
解决办法
1196
查看次数

Visual Studio 编译器在简单 if 语句的分支预测方面有多好?

下面是一些 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?如果循环迭代次数很大,并且无法跳出循环,则这可能会对性能产生显着影响。

我通常会忽略这些情况,只是将信心寄托在编译器上,但如果能确切地知道它如何处理这些情况,那就太好了。

c++ visual-studio branch-prediction

0
推荐指数
1
解决办法
363
查看次数

为什么内联汇编不需要像 .data 或 .text 这样的节指令

作为一个新手,我正在遵循教程。一种是在 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。

x86 assembly inline-assembly

0
推荐指数
1
解决办法
432
查看次数

alloca() 如何与其他堆栈分配交互?

让我们从一个简单的堆栈分配示例开始:

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, …

c x86 alloca stack-frame

0
推荐指数
1
解决办法
182
查看次数

为什么这段代码打印由 clang 和 gcc 编译的不同值?

汇编:

.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。

c assembly x86-64 calling-convention

0
推荐指数
1
解决办法
86
查看次数

编译器可以生成无用的汇编代码吗?

我试图找到从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)

c x86 assembly gcc

-1
推荐指数
1
解决办法
165
查看次数

ARM 与 RISC 以及 x86 与 CISC

通过一些研究,我意识到 ARM 和 RISC 几乎可以互换使用,x86 和 CISC 也是如此。我理解RISC和CISC是架构。我的理解是,架构(即 RISC 或 CISC)是一种指令集,进程必须能够执行这些指令才能成为这些架构之一。例如,RISC-V 有一个它可以执行的指令列表,CISC 有一个它可以执行的指令列表。要成为 RISC 或 CISC,处理器必须能够执行特定的指令列表之一。但是,我不明白ARM和RISC以及x86和CISC分别有什么区别。ARM和x86不也是架构吗?我经常读到“ARM 架构”或“x86 架构”。感谢您为我澄清这一点。

x86 arm cpu-architecture risc

-1
推荐指数
2
解决办法
2479
查看次数

了解if(a> = 3)的gcc输出

在此输入图像描述

我认为因为条件是> = 3,我们应该使用jl(更少).

但gcc使用jle(少或相等).

这对我没有意义; 为什么编译器会这样做?

c x86 assembly gcc

-2
推荐指数
1
解决办法
131
查看次数