相关疑难解决方法(0)

C++标准是否允许未初始化的bool使程序崩溃?

我知道C++ 中的"未定义行为"几乎可以让编译器做任何想做的事情.但是,我遇到了让我感到惊讶的崩溃,因为我认为代码足够安全.

在这种情况下,真正的问题仅发生在使用特定编译器的特定平台上,并且仅在启用了优化时才发生.

我尝试了几件事来重现问题并将其简化到最大程度.这是一个名为的函数的摘录Serialize,它将获取bool参数,并将字符串true或复制false到现有的目标缓冲区.

如果bool参数是未初始化的值,那么这个函数是否会在代码审查中,没有办法告诉它实际上可能会崩溃?

// Zero-filled global buffer of 16 characters
char destBuffer[16];

void Serialize(bool boolValue) {
    // Determine which string to print based on boolValue
    const char* whichString = boolValue ? "true" : "false";

    // Compute the length of the string we selected
    const size_t len = strlen(whichString);

    // Copy string into destination buffer, which is zero-filled (thus already null-terminated)
    memcpy(destBuffer, whichString, len);
}
Run Code Online (Sandbox Code Playgroud)

如果使用clang 5.0.0 +优化执行此代码,它将/可能崩溃.

boolValue ? "true" …

c++ abi llvm undefined-behavior llvm-codegen

482
推荐指数
5
解决办法
3万
查看次数

为什么引入无用的MOV指令会加速x86_64汇编中的紧凑循环?

背景:

在使用嵌入式汇编语言优化某些Pascal代码时,我注意到了一条不必要的MOV指令,并将其删除.

令我惊讶的是,删除不必要的指令会导致我的程序变慢.

我发现添加任意无用的MOV指令可以进一步提高性能.

效果不稳定,并且基于执行顺序进行更改:相同的垃圾指令向上或向下移动一行会产生减速.

我知道CPU会进行各种优化和精简,但这看起来更像是黑魔法.

数据:

我的代码版本有条件地在运行时间的循环中编译三个垃圾操作2**20==1048576.(周围的程序只计算SHA-256哈希值).

在我相当老的机器(英特尔(R)Core(TM)2 CPU 6400 @ 2.13 GHz)上的结果:

avg time (ms) with -dJUNKOPS: 1822.84 ms
avg time (ms) without:        1836.44 ms
Run Code Online (Sandbox Code Playgroud)

程序在循环中运行25次,每次运行顺序随机变化.

摘抄:

{$asmmode intel}
procedure example_junkop_in_sha256;
  var s1, t2 : uint32;
  begin
    // Here are parts of the SHA-256 algorithm, in Pascal:
    // s0 {r10d} := ror(a, 2) xor ror(a, 13) xor …
Run Code Online (Sandbox Code Playgroud)

optimization performance assembly freepascal x86-64

217
推荐指数
3
解决办法
3万
查看次数

为什么32位寄存器上的x86-64指令归零整个64位寄存器的上半部分?

x86-64 Tour of Intel Manuals中,我读到了

也许最令人惊讶的事实是,诸如MOV EAX, EBX自动将指令的高32位归零的指令RAX.

同一来源引用的英特尔文档(3.4.1.1 64位手动基本架构中的通用寄存器)告诉我们:

  • 64位操作数在目标通用寄存器中生成64位结果.
  • 32位操作数生成32位结果,在目标通用寄存器中零扩展为64位结果.
  • 8位和16位操作数生成8位或16位结果.目标通用寄存器的高56位或48位(分别)不会被操作修改.如果8位或16位操作的结果用于64位地址计算,则将寄存器显式符号扩展为完整的64位.

在x86-32和x86-64汇编中,16位指令如

mov ax, bx
Run Code Online (Sandbox Code Playgroud)

不要表现出这种"奇怪"的行为,即eax的上层词被归零.

因此:引入这种行为的原因是什么?乍一看似乎不合逻辑(但原因可能是我习惯了x86-32汇编的怪癖).

x86 assembly x86-64 cpu-registers zero-extension

97
推荐指数
3
解决办法
2万
查看次数

x86_64寄存器rax/eax/ax/al覆盖完整寄存器内容

正如广泛宣传的那样,现代x86_64处理器具有64位寄存器,可以以向后兼容的方式用作32位寄存器,16位寄存器甚至8位寄存器,例如:

0x1122334455667788
  ================ rax (64 bits)
          ======== eax (32 bits)
              ====  ax (16 bits)
              ==    ah (8 bits)
                ==  al (8 bits)
Run Code Online (Sandbox Code Playgroud)

这样的方案可以从字面上理解,即,总是可以使用指定的名称仅访问寄存器的一部分用于读取或写入目的,并且这将是高度逻辑的.实际上,对于高达32位的所有内容都是如此:

mov  eax, 0x11112222 ; eax = 0x11112222
mov  ax, 0x3333      ; eax = 0x11113333 (works, only low 16 bits changed)
mov  al, 0x44        ; eax = 0x11113344 (works, only low 8 bits changed)
mov  ah, 0x55        ; eax = 0x11115544 (works, only high 8 bits changed)
xor  ah, ah          ; eax = 0x11110044 (works, only high 8 …
Run Code Online (Sandbox Code Playgroud)

assembly x86-64 cpu-registers zero-extension

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

35
推荐指数
2
解决办法
5万
查看次数

cltq在装配中做了什么?

0x0000000000400553 <main+59>:   mov    -0x4(%rbp),%eax
0x0000000000400556 <main+62>:   cltq   
0x0000000000400558 <main+64>:   shl    $0x3,%rax
0x000000000040055c <main+68>:   mov    %rax,%rdx
Run Code Online (Sandbox Code Playgroud)

事实上,我的程序很简单:

5   int main(int argc, char *argv[]) { 
6     int i = 0;
7     while(environ[i]) {
8       printf("%s\n", environ[i++]);
9     }
10    return 0;
Run Code Online (Sandbox Code Playgroud)

但是程序集输出很长:

Dump of assembler code for function main:
0x0000000000400518 <main+0>:    push   %rbp
0x0000000000400519 <main+1>:    mov    %rsp,%rbp
0x000000000040051c <main+4>:    sub    $0x20,%rsp
0x0000000000400520 <main+8>:    mov    %edi,-0x14(%rbp)
0x0000000000400523 <main+11>:   mov    %rsi,-0x20(%rbp)
0x0000000000400527 <main+15>:   movl   $0x0,-0x4(%rbp)
0x000000000040052e <main+22>:   jmp    0x400553 <main+59>
0x0000000000400530 <main+24>:   mov    -0x4(%rbp),%eax …
Run Code Online (Sandbox Code Playgroud)

x86 assembly gnu-assembler att

26
推荐指数
4
解决办法
4万
查看次数

x86的MOV真的可以"免费"吗?为什么我不能重现这个呢?

我一直看到人们声称MOV指令可以在x86中免费,因为寄存器重命名.

对于我的生活,我无法在一个测试用例中验证这一点.每个测试用例我尝试揭穿它.

例如,这是我用Visual C++编译的代码:

#include <limits.h>
#include <stdio.h>
#include <time.h>

int main(void)
{
    unsigned int k, l, j;
    clock_t tstart = clock();
    for (k = 0, j = 0, l = 0; j < UINT_MAX; ++j)
    {
        ++k;
        k = j;     // <-- comment out this line to remove the MOV instruction
        l += j;
    }
    fprintf(stderr, "%d ms\n", (int)((clock() - tstart) * 1000 / CLOCKS_PER_SEC));
    fflush(stderr);
    return (int)(k + j + l);
}
Run Code Online (Sandbox Code Playgroud)

这为循环生成以下汇编代码(随意生成这个你想要的;你显然不需要Visual C++):

LOOP:
    add edi,esi
    mov …
Run Code Online (Sandbox Code Playgroud)

c x86 assembly cpu-registers micro-optimization

23
推荐指数
2
解决办法
2113
查看次数

为什么GCC不使用部分寄存器?

write(1,"hi",3)在linux上反汇编,gcc -s -nostdlib -nostartfiles -O3结果如下:

ba03000000     mov edx, 3 ; thanks for the correction jester!
bf01000000     mov edi, 1
31c0           xor eax, eax
e9d8ffffff     jmp loc.imp.write
Run Code Online (Sandbox Code Playgroud)

我不是到编译器的开发,但由于移动到这些寄存器的每一个值是恒定的和已知的编译时间,我很好奇,为什么不GCC使用dl,dilal来代替.也许有人会说,此功能不会让任何性能上的差异,但有一个在之间的可执行文件的大小有很大的区别mov $1, %rax => b801000000,并mov $1, %al => b001当我们谈论数千寄存器的程序访问.如果软件的优雅部分不仅体积小,它确实会对性能产生影响.

有人可以解释为什么"海湾合作委员会决定"它无所谓?

x86 assembly gcc x86-64

13
推荐指数
2
解决办法
1655
查看次数

程序集cltq和movslq的区别

计算机系统的第3章程序员的观点(第2版)提到了
cltq相当于movslq %eax, %rax.

为什么他们创建一个新的指令(cltq)而不是仅使用movslq %eax,%rax?这不是多余的吗?

x86 assembly x86-64 instruction-set att

7
推荐指数
1
解决办法
2万
查看次数

x86-64 平台上的 int_fast8_t 大小与 int_fast16_t 大小

我已经了解到,在 x86-64 平台上使用任何 64 位寄存器都需要一个REX前缀,而任何小于 64 位的地址都需要一个地址大小前缀。

在 x86-64 位上:

E3rel8 是jrcxz

67 E3rel8 是jecxz

67是地址大小覆盖前缀的操作码。

sizeof(int_fast8_t)是 8 位,而其他sizeof(int_fast16_t)sizeof(int_fast32_t)(仅在 Linux 上)是 64 位。

为什么其他快速类型定义是 64 位而只有int_fast8_t8 位?

和对齐有关系吗?

c 64-bit assembly x86-64 low-level

3
推荐指数
1
解决办法
232
查看次数