相关疑难解决方法(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万
查看次数

为什么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万
查看次数

Clang 和 GCC 对 movzx 的奇怪使用

我知道这movzx可以用于打破依赖关系,但我偶然发现了movzxClang 和 GCC 的一些用途,我真的看不出它们有什么用处。这是我在 Godbolt 编译器浏览器上尝试的一个简单示例:

#include <stdint.h>

int add2bytes(uint8_t* a, uint8_t* b) {
    return uint8_t(*a + *b);
}
Run Code Online (Sandbox Code Playgroud)

与海湾合作委员会 12 -O3

add2bytes(unsigned char*, unsigned char*):
        movzx   eax, BYTE PTR [rsi]
        add     al, BYTE PTR [rdi]
        movzx   eax, al
        ret
Run Code Online (Sandbox Code Playgroud)

如果我理解正确的话,这里的第一个movzx打破了对先前eax值的依赖,但第二个是什么movzx做什么?我认为它不会破坏任何依赖关系,也不应该影响结果。

使用 clang 14 -O3,情况更加奇怪:

add2bytes(unsigned char*, unsigned char*):                       # @add2bytes(unsigned char*, unsigned char*)
        mov     al, byte ptr [rsi]
        add     al, byte ptr [rdi]
        movzx   eax, al
        ret …
Run Code Online (Sandbox Code Playgroud)

c++ x86 assembly gcc clang

53
推荐指数
2
解决办法
3028
查看次数

使用gcc时,是什么原因导致char被签名或取消签名?

如果char在C(使用gcc)中签名或未签名,会导致什么?我知道,标准并没有规定一个比其他,我也可以检查CHAR_MIN,并CHAR_MAX从limits.h中,但我想知道是什么原因引发了另一种使用GCC时

如果我从libgcc-6读取limits.h,我看到有一个宏__CHAR_UNSIGNED__定义了一个"默认"char签名或无符号但我不确定这是由编译器在(他)的构建时间设置的.

我试图列出GCC预定义的makros

$ gcc -dM -E -x c /dev/null | grep -i CHAR
#define __UINT_LEAST8_TYPE__ unsigned char
#define __CHAR_BIT__ 8
#define __WCHAR_MAX__ 0x7fffffff
#define __GCC_ATOMIC_CHAR_LOCK_FREE 2
#define __GCC_ATOMIC_CHAR32_T_LOCK_FREE 2
#define __SCHAR_MAX__ 0x7f
#define __WCHAR_MIN__ (-__WCHAR_MAX__ - 1)
#define __UINT8_TYPE__ unsigned char
#define __INT8_TYPE__ signed char
#define __GCC_ATOMIC_WCHAR_T_LOCK_FREE 2
#define __CHAR16_TYPE__ short unsigned int
#define __INT_LEAST8_TYPE__ signed char
#define __WCHAR_TYPE__ int
#define __GCC_ATOMIC_CHAR16_T_LOCK_FREE 2
#define __SIZEOF_WCHAR_T__ 4
#define __INT_FAST8_TYPE__ signed char
#define …
Run Code Online (Sandbox Code Playgroud)

c gcc

49
推荐指数
5
解决办法
5914
查看次数

编译器中的布尔值为8位.对他们的操作是否效率低下?

我正在阅读Agner Fog的" 用C++优化软件 "(特定于英特尔,AMD和威盛的x86处理器),它在第34页说明

布尔变量存储为8位整数,值0表示false,1表示true.布尔变量是超定的,因为所有具有布尔变量作为输入的运算符检查输入是否具有除0或1之外的任何其他值,但是具有布尔值作为输出的运算符不能产生除0或1之外的其他值.布尔变量作为输入效率低于必要的效率.

这今天仍然适用于编译器吗?你能举个例子吗?作者说

如果确定操作数没有除0和1之外的其他值,则可以使布尔运算更有效.编译器没有做出这样的假设的原因是变量可能具有其他值,如果它们是未初始化或来自不明来源.

这是否意味着如果我拿一个函数指针bool(*)()作为示例并调用它,那么对它的操作会产生效率低下的代码?或者是通过取消引用指针或从引用读取然后对其进行操作来访问布尔值的情况?

c c++ optimization x86 boolean

46
推荐指数
2
解决办法
3608
查看次数

x86-64 System V ABI在哪里记录?

x86-64 System V ABI(用于除Windows之外的所有内容)过去常常访问http://x86-64.org/documentation/abi.pdf,但该网站现已脱离互联网.

该文件是否有新的权威主页?

linux assembly x86-64 abi calling-convention

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

为什么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
查看次数

x86程序集中堆栈对齐的响应性

我试图清楚地了解谁(调用者或被调用者)负责堆栈对齐.64位汇编的情况相当清楚,它是由调用者.

参考System V AMD64 ABI,第3.2.2节"堆栈帧":

输入参数区域的末尾应在16(32,如果在堆栈上传递__m256)字节边界上对齐.

换句话说,应该可以安全地假设,对于被调用函数的每个入口点:

16 | (%rsp + 8)

保持(额外八个是因为call隐含地在堆栈上推送返回地址).

它在32位世界中的表现(假设是cdecl)?我注意到,使用以下构造gcc将对齐放置在被调用函数:

and esp, -16
Run Code Online (Sandbox Code Playgroud)

这似乎表明,这是被召唤者的责任.

为了更清楚,请考虑以下代码:

global main
extern printf
extern scanf
section .rodata
    s_fmt   db "%d %d", 0
    s_res   db `%d with remainder %d\n`, 0
section .text
main:
    start   0, 0
    sub     esp, 8
    mov     DWORD [ebp-4], 0 ; dividend
    mov     DWORD [ebp-8], 0 ; divisor

    lea     eax, [ebp-8]
    push …
Run Code Online (Sandbox Code Playgroud)

linux x86 assembly gcc memory-alignment

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

在x86-64 SysV ABI中,高位参数和返回值寄存器是否允许垃圾?

除了其他方面,x86-64 SysV ABI指定了如何在寄存器中传递函数参数(第一个参数in rdi,then rsi等等),以及如何传回整数返回值(in rax和then rdx表示非常大的值).

然而,我找不到的是当传递小于64位的类型时,参数或返回值寄存器的高位应该是什么.

例如,对于以下功能:

void foo(unsigned x, unsigned y);
Run Code Online (Sandbox Code Playgroud)

... x将被传入rdiyrsi,但他们只是32位.不要的高32位rdirsi必须为零?直观地说,我会假设是,但是所有gcc,clang和icc 生成代码mov在开始时都有特定的指令将高位清零,所以看起来编译器假定不然.

类似地,编译器似乎假设rax如果返回值小于64 位,则返回值的高位可能具有垃圾位.例如,以下代码中的循环:

unsigned gives32();
unsigned short gives16();

long sum32_64() {
  long total = 0;
  for (int i=1000; i--; ) {
    total += gives32();
  }
  return total;
}

long sum16_64() {
  long total = 0;
  for (int i=1000; i--; ) {
    total += …
Run Code Online (Sandbox Code Playgroud)

linux x86 x86-64 calling-convention

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

为什么ARM使用两条指令来掩盖值?

对于以下功能......

uint16_t swap(const uint16_t value)
{
    return value << 8 | value >> 8;
}
Run Code Online (Sandbox Code Playgroud)

...为什么带有-O2的ARM gcc 6.3.0会产生以下程序集?

swap(unsigned short):
  lsr r3, r0, #8
  orr r0, r3, r0, lsl #8
  lsl r0, r0, #16         # shift left
  lsr r0, r0, #16         # shift right
  bx lr
Run Code Online (Sandbox Code Playgroud)

似乎编译器使用两个移位来屏蔽不需要的字节,而不是使用逻辑AND.编译器可以改用and r0, r0, #4294901760吗?

c++ assembly gcc arm

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