gpt4 book ai didi

arrays - 为什么 Rust 以特定于体系结构的方式生成 LLVM IR,并对可变静态变量使用空数组?

转载 作者:行者123 更新时间:2023-12-01 03:03:02 26 4
gpt4 key购买 nike

struct Point<'a> {
x: i32,
caption: &'a str,
y: i32,
}

static mut global_var: Point = Point {
x: 123,
y: 344,
caption: "123",
};

对应的LLVM IR是:
%Point = type
{
[0 x i64],
{ [0 x i8]*, i64 },
[0 x i32],
i32,
[0 x i32],
i32,
[0 x i32]
}

@_ZN5hello10global_var17h76c725a117a5fdc6E = internal global
<{ i8*, [16 x i8] }>
<{
i8* getelementptr inbounds
(
<{ [3 x i8] }>,
<{ [3 x i8] }>* @6,
i32 0,
i32 0,
i32 0
),
[16 x i8] c"\03\00\00\00\00\00\00\00{\00\00\00X\01\00\00"
}>,
align 8,
!dbg !330

我试图找到两个有趣的问题的答案:
  • 为什么%Point的类型定义中有空数组?这里似乎不是 Pascal 风格的数组。
  • 为什么是global_var以间接和特定于体系结构的方式初始化(​​整数的内容直接用小字节序缓冲区填充),因为 LLVM IR 代码应该在体系结构上独立?

  • 如果可能,我们能否以更具可读性的方式获得带有这些初始化的 LLVM IR 代码?

    更新以回应一些评论:
  • 为什么 c"\03\00\00\00\00\00\00\00{\00\00\00X\01\00\00"是结构的初始化?

  • 如果我们记下字符串的十六进制表示,我们可以发现它正是 i64 3、i32 123 和 i23 344 在小端架构中的存储方式。
  • 我的 Rust 版本是 1.41.0-nightly (19a0de242 2019-12-12)。 LLVM IR 由 cargo rustc -- --emit=llvm-bc 生成然后使用 llvm-dis拆卸。
  • 最佳答案

    免责声明:这个答案是有根据的猜测。
    TL;DR:我想显式填充数组和显式初始化旨在避免留下任何未初始化的字节,以及由此导致的未定义行为。

    绕道C
    重要的是要意识到 LLVM 从 C 继承了它的许多低级语义。毕竟,它的第一个也是最重要的前端是 Clang,这已经塑造了它的大部分。
    当 Clang 降低 struct到 LLVM IR,它自信地让 LLVM 找出填充 link .
    因此:

        struct A
    {
    int a;
    struct { char const* ptr; size_t len; } str;
    char c;
    };

    A const GLOBAL{ 1, { "hello", 5 }, 'c' };
    降低为:
    %struct.A = type { i32, %struct.anon, i8 }
    %struct.anon = type { i8*, i64 }


    @_ZL6GLOBAL = internal constant %struct.A
    {
    i32 1,
    %struct.anon
    {
    i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str, i32 0, i32 0),
    i64 5
    },
    i8 99
    }, align 8

    @.str = private unnamed_addr constant [6 x i8] c"hello\00", align 1
    这意味着填充字节未初始化,并且在典型的 C 方式中读取未初始化的字节是未定义的行为。
    这意味着使用未初始化的填充字节对结构进行位复制是未定义行为,而 memcpy调用(被降低到内部函数)似乎没有受到影响,我不知道 C 标准中有任何规定提供 memcpy通过...

    回到 Rust
    每当出现未定义行为时,Rust 都会采取强硬立场:
  • 在安全的 Rust1 中不应该有未定义的行为。
  • 在不安全的 Rust 中应该有尽可能少的未定义行为。

  • 留下未初始化的填充字节,并让用户执行位复制,这似乎是未定义行为的不必要来源:
    似乎没有太多(如果有的话)性能优势:由于 Rust 可以自由地重新排列结构成员和压缩结构,因此填充字节通常很少(只有几个尾随字节)。
    因此,我的猜测是 rustc 显式指定了填充数组 2 并显式初始化它们,以避免留下任何未初始化的填充字节。
    1 有,还是有。例如,由于 LLVM 考虑转换 floatint如果值不合适,则是 UB,或者考虑到没有副作用的无限循环的 LLVM 是 UB——两者都是从 C 继承的。这是一项正在进行的工作。
    2 这并没有为 0 大小的数组提供基本原理,这些对我来说似乎完全是多余的。

    关于arrays - 为什么 Rust 以特定于体系结构的方式生成 LLVM IR,并对可变静态变量使用空数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59888944/

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