相关疑难解决方法(0)

在C++内联asm中使用基指针寄存器

我希望能够%rbp在内联asm中使用基指针寄存器().这样的玩具示例是这样的:

void Foo(int &x)
{
    asm volatile ("pushq %%rbp;"         // 'prologue'
                  "movq %%rsp, %%rbp;"   // 'prologue'
                  "subq $12, %%rsp;"     // make room

                  "movl $5, -12(%%rbp);" // some asm instruction

                  "movq %%rbp, %%rsp;"  // 'epilogue'
                  "popq %%rbp;"         // 'epilogue'
                  : : : );
    x = 5;
}

int main() 
{
    int x;
    Foo(x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我希望,因为我使用通常的序幕/结尾函数调用方法来推送和弹出旧的%rbp,这样就可以了.但是,当我尝试在内x联asm之后访问时,它会出现故障.

GCC生成的汇编代码(略微剥离)是:

_Foo:
    pushq   %rbp
    movq    %rsp, %rbp
    movq    %rdi, -8(%rbp)

    # INLINEASM
    pushq %rbp;          // prologue
    movq %rsp, …
Run Code Online (Sandbox Code Playgroud)

c++ x86 assembly red-zone

13
推荐指数
1
解决办法
4119
查看次数

如果只需要结果的低部分,那么可以使用哪个2的补码整数运算而不将输入中的高位置零?

在汇编编程中,想要从寄存器的低位计算某些东西是相当普遍的,这些位不能保证将其他位置零.在像C这样的高级语言中,你只需将输入转换为小尺寸,让编译器决定是否需要分别将每个输入的高位归零,或者是否可以在输出之后切断结果的高位.事实.

这是为x86-64的(又名AMD64),出于各种原因尤其常见1,其中的一些是存在于其它的ISA.

我将使用64位x86作为示例,但目的是询问/讨论2的补码和无符号二进制算法,因为所有现代CPU都使用它.(注意,C和C++不保证两个补码4,并且有符号溢出是未定义的行为.)

作为示例,考虑一个可以编译为LEA指令2的简单函数.(在X86-64 SysV的(Linux)的ABI 3,前两个函数参数是rdirsi,与在返回rax. int是一个32位的类型.)

; int intfunc(int a, int b) { return a + b*4 + 3; }
intfunc:
    lea  eax,  [edi + esi*4 + 3]  ; the obvious choice, but gcc can do better
    ret
Run Code Online (Sandbox Code Playgroud)

gcc知道即使是负有符号整数,加法也只是从右到左,所以输入的高位不会影响进入的内容eax.因此,它保存了一个指令字节并使用 lea eax, [rdi + rsi*4 + 3]

哪些其他操作具有结果低位的这种属性而不依赖于输入的高位?

为什么它有效?



脚注

1为什么x86-64频繁出现这种情况:x86-64有可变长度指令,其中额外的前缀字节改变了操作数大小(从32到64或16),因此在指令中通常可以保存一个字节.以相同的速度执行.当写入低8b或16b的寄存器(或稍后读取完整寄存器(Intel pre-IvB)时的失速)时,它也具有错误依赖性(AMD/P4/Silvermont):由于历史原因, …

binary x86 assembly integer twos-complement

12
推荐指数
1
解决办法
928
查看次数

仅遍历第一个和最后一个元素

给定N元素,仅处理第一个(0)和最后一个(N-1)元素.

但是,如果N = 1只处理单个元素一次.

使用一个或两次运行的循环,可以避免重复循环体.如果有一种可读的方法,那么它对源代码大小有好处.如果循环体很大,它也可能对机器代码大小有好处,并且编译器最终不会复制它.


我试过递增N-1但是当N=1(永远循环)时它不起作用.有没有技巧(反向循环fi)来解决这个问题?

for (i = 0 ; i < N ; i += (N - 1))
Run Code Online (Sandbox Code Playgroud)

编辑:

我原来的问题涉及x,y,z方向的三个嵌套循环,这就是为什么我不能只处理elem [0])和elem [N-1].现在我有以下内容

#define forEachLglBound(i_,j_,k_)                                   \
        for(Int i_ = 0;i_ < NPX;i_+=((NPX>1) ? (NPX-1) : 1))        \
            for(Int j_ = 0;j_ < NPY;j_+=((NPY>1) ? (NPY-1) : 1))    \
                for(Int k_ = 0;k_ < NPZ;k_+=((NPZ>1) ? (NPZ-1) : 1))
Run Code Online (Sandbox Code Playgroud)

c loops for-loop

8
推荐指数
1
解决办法
537
查看次数

使用GNU C内联汇编在VGA内存中绘制字符

我正在学习用DOS和内联汇编在DOS下做一些低级VGA编程.现在我正在尝试创建一个在屏幕上打印出一个角色的功能.

这是我的代码:

//This is the characters BITMAPS
uint8_t characters[464] = {
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x50,
  0x50,0x00,0x00,0x00,0x00,0x00,0x50,0xf8,0x50,0x50,0xf8,0x50,0x00,0x20,0xf8,0xa0,
  0xf8,0x28,0xf8,0x00,0xc8,0xd0,0x20,0x20,0x58,0x98,0x00,0x40,0xa0,0x40,0xa8,0x90,
  0x68,0x00,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x20,0x40,0x40,0x40,0x40,0x20,0x00,
  0x20,0x10,0x10,0x10,0x10,0x20,0x00,0x50,0x20,0xf8,0x20,0x50,0x00,0x00,0x20,0x20,
  0xf8,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x60,0x20,0x40,0x00,0x00,0x00,0xf8,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x08,0x10,0x20,0x40,0x80,
  0x00,0x70,0x88,0x98,0xa8,0xc8,0x70,0x00,0x20,0x60,0x20,0x20,0x20,0x70,0x00,0x70,
  0x88,0x08,0x70,0x80,0xf8,0x00,0xf8,0x10,0x30,0x08,0x88,0x70,0x00,0x20,0x40,0x90,
  0x90,0xf8,0x10,0x00,0xf8,0x80,0xf0,0x08,0x88,0x70,0x00,0x70,0x80,0xf0,0x88,0x88,
  0x70,0x00,0xf8,0x08,0x10,0x20,0x20,0x20,0x00,0x70,0x88,0x70,0x88,0x88,0x70,0x00,
  0x70,0x88,0x88,0x78,0x08,0x70,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x00,0x30,0x30,
  0x00,0x30,0x10,0x20,0x00,0x00,0x10,0x20,0x40,0x20,0x10,0x00,0x00,0xf8,0x00,0xf8,
  0x00,0x00,0x00,0x00,0x20,0x10,0x08,0x10,0x20,0x00,0x70,0x88,0x10,0x20,0x00,0x20,
  0x00,0x70,0x90,0xa8,0xb8,0x80,0x70,0x00,0x70,0x88,0x88,0xf8,0x88,0x88,0x00,0xf0,
  0x88,0xf0,0x88,0x88,0xf0,0x00,0x70,0x88,0x80,0x80,0x88,0x70,0x00,0xe0,0x90,0x88,
  0x88,0x90,0xe0,0x00,0xf8,0x80,0xf0,0x80,0x80,0xf8,0x00,0xf8,0x80,0xf0,0x80,0x80,
  0x80,0x00,0x70,0x88,0x80,0x98,0x88,0x70,0x00,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,
  0x70,0x20,0x20,0x20,0x20,0x70,0x00,0x10,0x10,0x10,0x10,0x90,0x60,0x00,0x90,0xa0,
  0xc0,0xa0,0x90,0x88,0x00,0x80,0x80,0x80,0x80,0x80,0xf8,0x00,0x88,0xd8,0xa8,0x88,
  0x88,0x88,0x00,0x88,0xc8,0xa8,0x98,0x88,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x70,
  0x00,0xf0,0x88,0x88,0xf0,0x80,0x80,0x00,0x70,0x88,0x88,0xa8,0x98,0x70,0x00,0xf0,
  0x88,0x88,0xf0,0x90,0x88,0x00,0x70,0x80,0x70,0x08,0x88,0x70,0x00,0xf8,0x20,0x20,
  0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x88,0x88,0x88,0x88,0x50,
  0x20,0x00,0x88,0x88,0x88,0xa8,0xa8,0x50,0x00,0x88,0x50,0x20,0x20,0x50,0x88,0x00,
  0x88,0x50,0x20,0x20,0x20,0x20,0x00,0xf8,0x10,0x20,0x40,0x80,0xf8,0x00,0x60,0x40,
  0x40,0x40,0x40,0x60,0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x30,0x10,0x10,0x10,
  0x10,0x30,0x00,0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,
  0x00,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8};
/**************************************************************************
 *  put_char                                                              *
 *     Print char                                                         *
 **************************************************************************/
void put_char(int x ,int y,int ascii_char ,byte color){

    __asm__(
        "push %si\n\t"
        "push %di\n\t"
        "push %cx\n\t"
        "mov color,%dl\n\t"   //test color
        "mov ascii_char,%al\n\t"  //test char
        "sub $32,%al\n\t"
        "mov $7,%ah\n\t"
        "mul %ah\n\t"
        "lea $characters,%si\n\t" …
Run Code Online (Sandbox Code Playgroud)

c x86 gcc dos djgpp

8
推荐指数
1
解决办法
1660
查看次数

使用按位AND和popcount而不是实际int或float的大(0,1)矩阵乘法?

对于乘法大二进制矩阵(10Kx20K),我通常要做的是将矩阵转换为浮点数并执行浮点矩阵乘法,因为整数矩阵乘法非常慢(请看这里).

但这一次,我需要执行超过数十万次这样的乘法运算,甚至平均事情上的毫秒级性能提升.


我想要一个intfloat矩阵作为结果,因为产品可能有非0或1的元素.输入矩阵元素都是0或1,因此它们可以存储为单个位.

在行向量和列向量之间的内积中(为了产生输出矩阵的一个元素),乘法简化为按位AND.添加仍然是添加,但我们可以添加具有填充计数功能的位,而不是单独循环它们.

一些其他布尔/二进制矩阵函数或位而不是计数它们,产生位矩阵结果,但这不是我需要的.


下面是一个示例代码,显示将问题形成为std::bitset, AND并且count操作比矩阵乘法更快.

#include <iostream>
using std::cout; using std::endl;
#include <vector>
    using std::vector;
#include <chrono>
#include <Eigen/Dense>
    using Eigen::Map; using Eigen::Matrix; using Eigen::MatrixXf;
#include <random>
    using std::random_device; using std::mt19937; using std::uniform_int_distribution;
#include <bitset>
    using std::bitset;

using std::floor;

const int NROW = 1000;
const int NCOL = 20000;

const float DENSITY = 0.4;
const float DENOMINATOR = 10.0 - (10*DENSITY);

void fill_random(vector<float>& vec) { …
Run Code Online (Sandbox Code Playgroud)

c++ sse avx bitset matrix-multiplication

8
推荐指数
1
解决办法
1145
查看次数

在预测现代超标量处理器上的操作延迟时需要考虑哪些因素以及如何手动计算它们?

我希望能够手动预测任意算术的长度(即没有分支或内存,尽管这也很好)x86-64汇编代码将采用特定的体系结构,考虑到指令重新排序,超标量,延迟,消费者价格指数等

什么/描述必须遵循的规则才能实现这一目标?


我想我已经找到了一些初步规则,但是我没有找到任何关于将任何示例代码分解为这个详细程度的引用,所以我不得不做一些猜测.(例如,英特尔优化手册甚至几乎没有提到指令重新排序.)

至少,我正在寻找(1)确认每条规则是正确的,或者是每条规则的正确陈述,以及(2)我可能忘记的任何规则的列表.

  • 每个循环发出尽可能多的指令,从当前循环开始按顺序开始,并且可能与重新排序缓冲区大小一样远.
  • 如果出现以下情况,可以在给定周期发出指令:
    • 没有影响其操作数的指令仍在执行中.和:
    • 如果它是浮点指令,则它之前的每个浮点指令都被发出(浮点指令具有静态指令重新排序).和:
    • 该循环有一个功能单元可用于该指令.每个(?)功能单元是流水线的,这意味着它可以在每个周期接受1个新指令,并且对于给定功能类的CPI,总功能单元的数量是1/CPI(这里模糊不清:可能是例如addps并且subps使用相同的功能) unit?我如何确定?).和:
    • 4此循环已经发出少于超标量宽度(通常)指令的数量.
  • 如果不能发出指令,则处理器不会发出任何称为"停顿"的条件.

例如,请考虑以下示例代码(计算交叉产品):

shufps   xmm3, xmm2, 210
shufps   xmm0, xmm1, 201
shufps   xmm2, xmm2, 201
mulps    xmm0, xmm3
shufps   xmm1, xmm1, 210
mulps    xmm1, xmm2
subps    xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

我试图预测Haswell的延迟看起来像这样:

; `mulps`  Haswell latency=5, CPI=0.5
; `shufps` Haswell latency=1, CPI=1
; `subps`  Haswell latency=3, CPI=1

shufps   xmm3, xmm2, 210   ; cycle  1
shufps   xmm0, xmm1, 201   ; cycle  2
shufps   xmm2, xmm2, 201   ; …
Run Code Online (Sandbox Code Playgroud)

assembly pipeline latency x86-64 superscalar

8
推荐指数
1
解决办法
268
查看次数

XOR交换算法中运算符的未定义行为?

void swap(int* a, int* b) {
    if (a != b)
        *a ^= *b ^= *a ^= *b;
}
Run Code Online (Sandbox Code Playgroud)

由于以上*a ^= *b ^= *a ^= *b只是一个快捷方式*a = *a ^ (*b = *b ^ (*a = *a ^ *b)),可以(例如)第二个*a被评估(对于XOR)就在第三个*a被修改之前(由=)?

我是否用C99/C11/C++ 98/C++ 11编写它是否重要?

c c++ swap operators undefined-behavior

5
推荐指数
1
解决办法
822
查看次数

跨多个整数计算位......有没有更快的方法?

我有一个包含 4 个 long 的数组,我想计算给定范围内设置位的数量。这是我目前正在使用的函数(其中bitcount(uint64_t)是一个内联 asm 函数,它给出了参数中设置的位数):

unsigned count_bits(const uint64_t *long_ptr, size_t begin, size_t end)
{
    uint64_t start_mask = ~((1L << (begin & 63)) - 1);
    uint64_t end_mask = ((1L << (end & 63)) - 1);
    if (begin >= 0 && begin < 64) {
        if (end < 64) {
            return bitcount(long_ptr[0] & start_mask & end_mask);
        } else if (end < 128) {
            return bitcount(long_ptr[0] & start_mask) + bitcount(long_ptr[1] & end_mask);
        } else if (end < 192) { …
Run Code Online (Sandbox Code Playgroud)

c++ performance bit-manipulation

5
推荐指数
1
解决办法
163
查看次数

如何从汇编程序指令转到C代码

我有一个任务,除其他外,我需要在.asm文件中查找某个指令和"反向工程"(找出)C代码的哪一部分导致它在汇编程序级别执行.(文字下方的例子)

什么是最快(最简单)的方法.或者更好地说,.asm文件中的其他命令/指令/标签应该/我可以注意什么,这将指导我使用正确的C代码?

我几乎没有使用汇编代码的经验,很难弄清楚C代码的确切行会导致特定指令发生.

如果这有任何区别,那么这个架构就是TriCore.

示例:通过跟踪使用变量的位置,我设法找出C代码在asm文件中导致插入的内容

 .L23:
    movh.a  a15,#@his(InsertStruct)
    ld.bu   d15,[a15]@los(InsertStruct)
    or  d15,#1
    st.b    [a15]@los(InsertStruct),d15
.L51:
    ld.bu   d15,[a15]@los(InsertStruct)
    insert  d15,d15,#0,#0,#1
    st.b    [a15]@los(InsertStruct),d15
.L17:
    mov d15,#-1
Run Code Online (Sandbox Code Playgroud)

这导致我得到以下C代码:

InsertStruct.SomeMember = 0x1u;

InsertStruct.SomeMember = 0x0u;
Run Code Online (Sandbox Code Playgroud)

c assembly instruction-set instructions

5
推荐指数
1
解决办法
687
查看次数

汇编中系统调用的返回值是多少?

当我尝试研究内核的系统调用的返回值时,我找到了描述它们的表,以及我需要将它放在不同的寄存器中以使它们工作.但是,我没有找到任何文档说明我从系统调用获得的返回值是什么.我只是在不同的地方找到我收到的东西将在EAX寄存器中.


TutorialsPoint:

结果通常在EAX寄存器中返回.

汇编语言循序渐进: Jeff Duntemann 编写的Linux书籍在他的程序中多次说明:

  • 查看sys_read在EAX中的返回值

  • 复制sys_read返回值以便安全保存


我没有解释任何有关此返回值的网站.有没有互联网资源?或者有人能解释我这个价值观吗?

linux assembly return-value nasm system-calls

4
推荐指数
1
解决办法
7758
查看次数