gpt4 book ai didi

webassembly - 理解类结构和构造函数调用

转载 作者:行者123 更新时间:2023-12-04 15:40:14 25 4
gpt4 key购买 nike

玩过循环、分支、表格和所有那些不错的操作符后,我几乎开始对这种语言感到满意,足以创造一些有用的东西,但有些逻辑我仍然不明白。请耐心等待,因为它会有点长。

问题:有人可以解释翻译的代码是如何工作的吗?我在下面进一步添加了具体问题。

首先这里是一些我一直在转换的琐碎的 C++ 代码:

class FirstClass {
int prop1 = 111;
int prop2 = 222;
int prop3 = 333;

public:
FirstClass(int param1, int param2) {
prop1 += param1 + param2;

}
};

class SecondClass {
public:
SecondClass() {

}
};

int main() {
FirstClass firstClass1(10, 5);
FirstClass firstClass2(30, 15);
FirstClass firstClass3(2, 4);
FirstClass firstClass4(2, 4);
}

翻译成:
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "main" (func $main))
(func $main (result i32)
(local $0 i32)
(i32.store offset=4
(i32.const 0)
(tee_local $0
(i32.sub
(i32.load offset=4
(i32.const 0)
)
(i32.const 64)
)
)
)
(drop
(call $_ZN10FirstClassC2Eii
(i32.add
(get_local $0)
(i32.const 48)
)
(i32.const 10)
(i32.const 5)
)
)
(drop
(call $_ZN10FirstClassC2Eii
(i32.add
(get_local $0)
(i32.const 32)
)
(i32.const 30)
(i32.const 15)
)
)
(drop
(call $_ZN10FirstClassC2Eii
(i32.add
(get_local $0)
(i32.const 16)
)
(i32.const 2)
(i32.const 4)
)
)
(drop
(call $_ZN10FirstClassC2Eii
(get_local $0)
(i32.const 2)
(i32.const 4)
)
)
(i32.store offset=4
(i32.const 0)
(i32.add
(get_local $0)
(i32.const 64)
)
)
(i32.const 0)
)
(func $_ZN10FirstClassC2Eii (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(i32.store offset=8
(get_local $0)
(i32.const 222)
)
(i32.store offset=4
(get_local $0)
(i32.const 222)
)
(i32.store
(get_local $0)
(i32.add
(i32.add
(get_local $1)
(get_local $2)
)
(i32.const 111)
)
)
(get_local $0)
)
)

所以现在我对这里实际发生的事情有一些疑问。虽然我认为我理解了大部分内容,但仍有一些事情我不确定:

例如,查看构造函数及其签名:
(func $_ZN10FirstClassC2Eii (param $0 i32) (param $1 i32) (param $2 i32) (result i32)

它有以下参数: (param $0 i32)我假设是在 main 函数中定义了一些局部的。让我们说一些内存。但是,我们知道 main 函数中有 4 个实例,这意味着所有这些实例都保存在同一个 (local $0 i32) 中。但是有不同的偏移量,我是对还是错?

接下来让我们看一下对构造函数的调用:
(drop
(call $_ZN10FirstClassC2Eii
(i32.add
(get_local $0)
(i32.const 32)
)
(i32.const 30)
(i32.const 15)
)
)

我们调用构造函数并传入 3 个参数。究竟是什么添加呢?我们是否在本地添加空间?仔细观察,对于每个构造函数调用,这个数字都会减少 16(我从上到下阅读代码),这大约是一个单词的大小。我不知道这是什么意思。

最后我们有:
(i32.store offset=4
(i32.const 0)
(tee_local $0
(i32.sub
(i32.load offset=4
(i32.const 0)
)
(i32.const 64)
)
)
)

它甚至加载了什么,为什么要减法?我的意思是它设置一个本地并返回它,以便我们可以将它存储在偏移量为 4 的线性内存中?偏移 4 与什么有关?

最佳答案

您注意到的很多内容都在 C++ 到某些编译器 IR 的转换中。由于您使用的工具基于 LLVM,如果您想进行探索,我建议您查看 LLVM 的 IR。 Here's your example, also unoptimized, in LLVM IR .这很有趣,因为 WebAssembly 发生在这个 LLVM IR 之后,所以你可以看到 C++ 的部分转换。也许我们可以理解它!

构造函数与 C++ 中的所有非静态函数类成员一样,具有隐式 *this范围。这就是第零个参数。为什么是这样i32 ?因为 WebAssembly 中的所有指针都是 i32 .

在 LLVM IR 中,这是:

define linkonce_odr void @FirstClass::FirstClass(int, int)(%class.FirstClass*, i32, i32) unnamed_addr #2 comdat align 2 !dbg !29 {

哪里 %class.FirstClass**this指针。稍后,当降低到 WebAssembly 时,它会变成 i32 .

对于您的以下问题......调用构造函数时有什么补充?我们必须创建 *this ,然后将它们分配到堆栈上。 LLVM 如此执行这些分配:
  %1 = alloca %class.FirstClass, align 4
%2 = alloca %class.FirstClass, align 4
%3 = alloca %class.FirstClass, align 4
%4 = alloca %class.FirstClass, align 4

所以它的堆栈思想包含四个 FirstClass 类型的变量。 .当我们降低到 WebAssembly 时,堆栈必须去某个地方。 WebAssembly 中有 3 个 C++ 堆栈可以放置的地方:
  • 在执行堆栈上(每个操作码都压入和弹出值,所以 add 弹出 2 然后压入 1)。
  • 作为本地人。
  • Memory .

  • 注意不能取1.和2的地址。构造函数传递 *this到一个函数,所以编译器必须将该值放在 Memory 上. Memory中的堆栈在哪里? Emscripten 为您处理!它决定将内存中的堆栈指针存储在地址 4,因此 (i32.load offset=4 (i32.const 0)) .四人 alloca然后来自 LLVM 位于该地址的偏移量处,因此 (i32.add (get_local $0) (i32.const 48))正在获取堆栈位置(我们在本地 $0 中加载)并获取其偏移量。这就是 *this 的值.

    请注意,优化后,绝大多数 C++ 堆栈上的变量不会在内存中结束!大多数将被推送/弹出,或存储在 WebAssembly 本地(其中有无穷大)。这类似于 x86 或 ARM 等其他 ISA:将本地变量放在寄存器中更好,但这些 ISA 只有少数几个。因为 WebAssembly 是一个虚拟 ISA,我们可以承受无限的局部变量,所以 LLVM/Emscripten 必须具体化到内存中的堆栈要小得多。它们必须被具体化的唯一时间是当它们的地址被获取时,或者它们通过引用传递(实际上是一个指针),或者一个函数有多个返回值(WebAssembly 将来可能会支持)。

    您拥有的最后一点代码:
  • 加载内存中的堆栈指针。
  • 从中减去 64。
  • 存储回堆栈指针。

  • 那是你的函数序言。如果您查看函数的最后部分,您会发现匹配的结尾将 64 添加回指针。这为四个人腾出了空间 alloca .它是(非官方)WebAssembly ABI 的一部分,每个函数负责为其变量增加和缩小内存中的堆栈。

    为什么是64?那是 4 x 16,刚好容纳这四个 FirstClass 的空间实例:它们各持有 3 i32存储时每个四舍五入到 16 个字节,以进行对齐。试用 sizeof(FirstClass)在 C++ 中(它是 12),然后尝试分配它们的数组(它们每个都将填充 4 个字节,以便每个条目对齐)。这只是 C++ 通常实现的一部分,与 LLVM 或 WebAssembly 无关。

    关于webassembly - 理解类结构和构造函数调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43571620/

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