gpt4 book ai didi

c++ - Clang 为指针减法生成异常代码,这是一个错误吗?

转载 作者:行者123 更新时间:2023-12-05 02:34:42 25 4
gpt4 key购买 nike

看看这段简单的代码,它计算了 eb 之间的数组元素数:

#include <cstddef>

struct Foo {
char z[3];
};

std::ptrdiff_t size(const Foo *b, const Foo *e) {
std::ptrdiff_t r = e - b;
//if (r < 0) __builtin_unreachable();
return r;
}

Clang 为此生成了很好的代码(使用 -O2):

size(Foo const*, Foo const*):                       # @size(Foo const*, Foo const*)
sub rsi, rdi
movabs rax, -6148914691236517205
imul rax, rsi
ret

但是,如果我取消对注释行的注释,我希望它不会更改 asm 代码。但确实如此,而且 clang 会生成更大且效率较低的代码:

size(Foo const*, Foo const*):                       # @size(Foo const*, Foo const*)
mov rax, rsi
sub rax, rdi
movabs rcx, -6148914691236517205
mul rcx
mov rax, rdx
shr rax
ret

这是一个错误吗?

类似的情况,看这段代码:

#include <cstddef>

struct Foo {
char z[3];
};

void bar(std::ptrdiff_t);

void foo(const Foo *b, const Foo *e) {
std::ptrdiff_t s = e - b;

for (std::ptrdiff_t i=0; i<s; i++) {
bar(i);
}
}

生成的代码是这样的:

foo(Foo const*, Foo const*):                        # @foo(Foo const*, Foo const*)
push r14
push rbx
push rax
sub rsi, rdi
test rsi, rsi
jle .LBB0_3
movabs rcx, -6148914691236517205
mov rax, rsi
mul rcx
shr rdx
cmp rdx, 2
mov r14d, 1
cmovge r14, rdx
xor ebx, ebx
.LBB0_2: # =>This Inner Loop Header: Depth=1
mov rdi, rbx
call bar(long)
add rbx, 1
cmp r14, rbx
jne .LBB0_2
.LBB0_3:
add rsp, 8
pop rbx
pop r14
ret

注意相同的元素数量计算效率较低。我认为发生这种情况是因为 clang 首先处理 e <b 的情况,所以它知道 s 是非负的,所以它应用与前一个案例相同的“优化”。

此外,还有与2cmov 的比较。这些不是没有必要的吗?这是什么把戏,还是错误?

神栓链接:https://godbolt.org/z/zMeY1MKh7

最佳答案

我会说问题出在这里(https://github.com/llvm-mirror/llvm/blob/master/lib/Target/X86/X86FastISel.cpp 的第 2942 行)

// FastISel doesn't have a pattern for all X86::MUL*r and X86::IMUL*r. Emit
// it manually.
if (BaseOpc == X86ISD::UMUL && !ResultReg) {
static const uint16_t MULOpc[] =
{ X86::MUL8r, X86::MUL16r, X86::MUL32r, X86::MUL64r };
static const MCPhysReg Reg[] = { X86::AL, X86::AX, X86::EAX, X86::RAX };
// First copy the first operand into RAX, which is an implicit input to
// the X86::MUL*r instruction.
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(TargetOpcode::COPY), Reg[VT.SimpleTy-MVT::i8])
.addReg(LHSReg);
ResultReg = fastEmitInst_r(MULOpc[VT.SimpleTy-MVT::i8],
TLI.getRegClassFor(VT), RHSReg);
} else if (BaseOpc == X86ISD::SMUL && !ResultReg) {
static const uint16_t MULOpc[] =
{ X86::IMUL8r, X86::IMUL16rr, X86::IMUL32rr, X86::IMUL64rr };
if (VT == MVT::i8) {
// Copy the first operand into AL, which is an implicit input to the
// X86::IMUL8r instruction.
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc,
TII.get(TargetOpcode::COPY), X86::AL)
.addReg(LHSReg);
ResultReg = fastEmitInst_r(MULOpc[0], TLI.getRegClassFor(VT), RHSReg);
} else
ResultReg = fastEmitInst_rr(MULOpc[VT.SimpleTy-MVT::i8],
TLI.getRegClassFor(VT), LHSReg, RHSReg);
}

第一个 if block 是关于无符号乘法(X86ISD::UMUL),第二个 else if block 是关于有符号乘法(X86ISD::SMUL)。

对于无符号乘法,首先在实际乘法之前发出 RAX(或其较短的变体)的拷贝。

对于操作数大小> 1 字节的有符号乘法,执行底部的小else block ,它直接进行有符号乘法。

对于 x86,imul 有一个双操作数形式(有符号乘法)- https://www.felixcloutier.com/x86/imul ,但不是 mul(无符号乘法)- https://www.felixcloutier.com/x86/mul .

用乘法交换除法和检测有符号与无符号的优化是在 LLVM 的架构独立部分完成的。 ( https://github.com/llvm/llvm-project/blob/main/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp(第 5108、5186、5334 行)。

对于无符号操作数低于 (0x8000000000000000) 的情况,x86 后端不会检查它是否可以使用带符号的乘法指令。

额外的右移指令是与寄存器复制不同的问题,寄存器复制也会发生在其他架构中,这些架构具有与有符号乘法指令一样具有表现力的无符号乘法指令(例如 ARM)。

根据您的建议,它与 BuildExactSDIV()(在 TargetLowering.cpp 的第 5108 行)相关,其中还包含:

  // Shift the value upfront if it is even, so the LSB is one.
if (UseSRA) {
// TODO: For UDIV use SRL instead of SRA.
SDNodeFlags Flags;
Flags.setExact(true);
Res = DAG.getNode(ISD::SRA, dl, VT, Res, Shift, Flags);
Created.push_back(Res.getNode());
}

应该注意有关如何为 UDIV 实现此操作的 TODO 注释。

BuildUDIV()(第 5334 行)中的无符号除法没有与 BuildSDIV(第 5190 行)类似的选项来调用 BuildExactSDIV() (5219)。不存在 BuildExactUDIV() 并且正常的构建总是涉及到偏移。

关于c++ - Clang 为指针减法生成异常代码,这是一个错误吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70718267/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com