LLVM语言将整数类型指定为iN,其中N是整数的位宽,范围从1到2 ^ 23-1(根据:http://llvm.org/docs/LangRef.html#integer-型)
我有两个问题:
将C程序编译为LLVM IR级别时,可以将哪些类型降低到i1,i2,i3等?看起来类型i8,i16,i32,i64必须足够,所以我想知道所有其他近800万个整数类型是什么.
将有符号和无符号整数类型都降低到i32是真的吗?是什么原因,为什么它不适用于32位浮点数(在LLVM中表示为f32)?
我正在尝试对一些Rust代码进行基准测试,但我无法弄清楚如何设置"ffast-math"选项.
% rustc -C opt-level=3 -C llvm-args='-enable-unsafe-fp-math' unrolled.rs
rustc: Unknown command line argument '-enable-unsafe-fp-math'. Try: 'rustc -help'
rustc: Did you mean '-enable-load-pre'?
Run Code Online (Sandbox Code Playgroud)
llvm-args='-ffast-math'并且llvm-args='-fast'也没有工作.我应该使用什么旗帜?
我写了一个简单的C++函数来检查编译器优化:
bool f1(bool a, bool b) {
return !a || (a && b);
}
Run Code Online (Sandbox Code Playgroud)
之后我检查了Rust中的等价物:
fn f1(a: bool, b: bool) -> bool {
!a || (a && b)
}
Run Code Online (Sandbox Code Playgroud)
我用godbolt检查汇编器输出.
C++代码的结果(由clang -O3标志编译)如下:
f1(bool, bool): # @f1(bool, bool)
xor dil, 1
or dil, sil
mov eax, edi
ret
Run Code Online (Sandbox Code Playgroud)
Rust等效的结果要长得多:
example::f1:
push rbp
mov rbp, rsp
mov al, sil
mov cl, dil
mov dl, cl
xor dl, -1
test dl, 1
mov byte ptr [rbp - 3], al
mov byte …Run Code Online (Sandbox Code Playgroud) LLVM 似乎忽略core::intrinsics::assume(..)调用。它们最终会出现在字节码中,但不会更改生成的机器码。例如,采用以下(无意义的)代码:
pub fn one(xs: &mut Vec<i32>) {
if let Some(x) = xs.pop() {
xs.push(x);
}
}
Run Code Online (Sandbox Code Playgroud)
这编译成一大堆程序集:
example::one:
push rbp
push r15
push r14
push r12
push rbx
mov rbx, qword ptr [rdi + 16]
test rbx, rbx
je .LBB0_9
mov r14, rdi
lea rsi, [rbx - 1]
mov qword ptr [rdi + 16], rsi
mov rdi, qword ptr [rdi]
mov ebp, dword ptr [rdi + 4*rbx - 4]
cmp rsi, qword ptr [r14 + 8]
jne …Run Code Online (Sandbox Code Playgroud) 我有一个用LLVM编写的编译器,我正在寻求提高我的ABI合规性.例如,我发现很难在Windows x86或Linux上找到C ABI的规范文档.我发现的那些用RAX/EAX /等解释它,而不是我可以使用的IR术语.
到目前为止,我认为我已经认为LLVM无形地对待聚合 - 也就是说,它将每个成员视为一个独特的参数.因此,例如,在Windows x64上,如果我想处理像文档所说的聚合,我将需要强制转换为该大小的单个整数,如8,16,32或64位.否则,通过指针传递.
对于Windows x86,似乎__cdecl和__stdcall不需要我的任何操作,因为所有参数都在堆栈上传递.__fastcall说前两个32位或更小的参数是寄存器传递的,所以我需要强制大小或更小的聚合.__thiscall在寄存器中传递它,其余的在堆栈中传递,所以看起来我不需要在这里执行任何调整.
对于__vectorcall,通过整数强制传递不超过sizeof(void*)的聚合.对于其他聚合,如果它们是HVAs,则按值传递; else在x86上传递值或在x64上传递指针.
这似乎很简单(相对而言),但是LLVM文档sext清楚地表明"这表明代码生成器应该将参数或返回值符号扩展到目标的ABI所需的范围(通常是32位)由调用者(对于参数)或被调用者(对于返回值)." x86调用约定的Microsoft页面没有提及任何扩展到任何宽度的内容.
我观察到Clang生成的LLVM IR byval在Windows上生成属性.我从上面收集到的理解从未要求byval使用.
如何将各种平台C ABI降低到LLVM IR?
我想看看一个小的Rust函数的程序集输出:
pub fn double(n: u8) -> u8 {
n + n
}
Run Code Online (Sandbox Code Playgroud)
我使用Godbolt Compiler Explorer来生成和查看程序集(-O当然还有标志).它显示了这个输出:
example::double:
push rbp
mov rbp, rsp
add dil, dil
mov eax, edi
pop rbp
ret
Run Code Online (Sandbox Code Playgroud)
现在我有点困惑,因为有这似乎并没有做任何有用的几个指令:push rbp,mov rbp, rsp和pop rbp.根据我的理解,我认为单独执行这三个指令没有任何副作用.那么为什么Rust优化器不会删除那些无用的指令呢?
为了比较,我还测试了一个C++版本:
unsigned char doubleN(unsigned char n) {
return n + n;
}
Run Code Online (Sandbox Code Playgroud)
程序集输出(带-O标志):
doubleN(unsigned char): # @doubleN(unsigned char)
add dil, dil
mov eax, edi
ret
Run Code Online (Sandbox Code Playgroud)
事实上,这里缺少上面那些"无用的"指令,正如我对优化输出所期望的那样.
从这个简单的C程序开始:
void nothing(void) {}
int main() {
int i;
for (i = 0; i < 10; ++i) {
nothing();
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我的传递输出如下:
注意:IR语句为绿色.
; Function Attrs: nounwind readnone ssp uwtable
define void @nothing() #0 {
entry:
ret void
}
; Function Attrs: nounwind readnone ssp uwtable
define i32 @main() #0 {
entry:
ret i32 0
}
Run Code Online (Sandbox Code Playgroud)
问:使用O3其认为是最高级别的优化,为什么没有nothing功能尚未消除的死代码?
我有一个功能,Rust的/ LLVM的优化失败并导致恐慌(在发布版本中),而未优化的代码(调试版本)工作正常.如果我比较生成的汇编代码,我甚至无法理解优化器试图完成的任务.(原因可能是这个函数使用内联汇编程序.)
有没有办法告诉Rust在优化过程中单独保留某些功能,还是必须关闭所有优化?
这是具体功能:
#[naked]
pub extern "C" fn dispatch_svc(){
Cpu::save_context();
let mut nr: u32 = 0;
unsafe {
asm!("ldr r0, [lr, #-4]
bic $0, r0, #0xff000000":"=r"(nr)::"r0":"volatile")
};
swi_service_routine(nr);
Cpu::restore_context_and_return();
}
Run Code Online (Sandbox Code Playgroud) 我有一个Box<dyn Any>,我知道基础类型,所以我想在Box::downcast()(源)中优化测试.
首先我试过std::hint::unreachable_unchecked():
pub unsafe fn downcast() -> Box<i32> {
let value = any();
if let Ok(value) = value.downcast() {
value
} else {
std::hint::unreachable_unchecked()
}
}
Run Code Online (Sandbox Code Playgroud)
和
pub unsafe fn downcast() -> Box<i32> {
any().downcast().map_err(|_| std::hint::unreachable_unchecked()).unwrap()
}
Run Code Online (Sandbox Code Playgroud)
与rustc -C opt-level=3两个结果在该(40行中省略):
example::downcast:
push rbx
sub rsp, 16
call any@PLT
mov rbx, rax
mov qword ptr [rsp], rax
mov qword ptr [rsp + 8], rdx
mov rdi, rax
call qword ptr …Run Code Online (Sandbox Code Playgroud) optimization assembly compiler-optimization rust llvm-codegen
在Blandy和Orendorff 的《Programming Rust》的第322页上是这样的说法:
...铁锈...认识到,有一种简单的方法可以将数字从1
n求和:总和始终等于n * (n+1) / 2。
这当然是相当众所周知的等效项,但是编译器如何识别它?我猜这是在LLVM优化过程中进行的,但是LLVM是否以某种方式从第一原理中推导了等效性,或者它只是具有一些可以简化为算术运算的“公共循环计算”?
compiler-construction compiler-optimization rust llvm-codegen
llvm-codegen ×10
rust ×7
llvm ×4
optimization ×3
assembly ×2
c ×2
abi ×1
clang ×1
fast-math ×1
llvm-clang ×1
llvm-ir ×1
performance ×1
types ×1
x86-64 ×1