val*_*ldo 7 c c++ clang webassembly
以下代码由clang编译到wasm平台:
uint64_t Get() const
{
uint32_t ord = -m_Order;
if (ord >= 64)
return 0;
return m_Num >> ord;
}
Run Code Online (Sandbox Code Playgroud)
编译后的 wasm 代码(启用优化)如下所示:
i64.const 0
local.get $l4
i32.const 0
local.get $p2
i32.sub
local.tee $l5
i64.extend_i32_u
i64.shr_u
local.get $l5
i32.const 63
i32.gt_u
select
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,表达式m_Num >> ord总是被求值,但最终结果将是 it 或0(第一个命令将其放在操作数堆栈上)。
我们在虚拟机上运行生成的 wasm 代码。问题是:当-m_Order大于63时,程序崩溃(即陷入困境,虚拟机不会崩溃),因为移位操作比操作数大小更多的位被认为是未定义的行为。
根据 LLVM 文档,这不是未定义的行为,它只是未定义的结果(所谓的毒害值)。
所以,我的问题是:是否有可能阻止 LLVM 进行这种优化?即不允许优化器产生未定义的结果,这些结果不应该由原始代码产生?
更新:
澄清:正如我们所发现的,WebAssembly 标准明确允许这样做。对于移位运算符,移位计数可以大于最大位数,在这种情况下,移位计数必须以最大位数为模进行处理(类似于 x86 处理器处理此问题的方式)。所以它甚至不是未定义的结果。
所以这是我们虚拟机的问题,它与标准有偏差。解决这个问题不是问题,但升级整个系统就有问题,所以我们更愿意保留当前的行为。我们需要一种解决该问题的方法。
我发现的一种解决方案是在块内调用一些第三方虚拟函数if,这将强制编译器生成正确的分支,而不是计算所有变体并通过select. 我的意思是:
uint64_t Get() const
{
uint32_t ord = -m_Order;
if (ord >= 64)
{
DummyFunction(); // this is the workaround
return 0;
}
return m_Num >> ord;
}
Run Code Online (Sandbox Code Playgroud)
但我想要一个更优雅的解决方案。毕竟,LLVM 在这方面应该非常灵活。它在某处包含目标平台的确切定义,其中包括它如何处理位移位的定义。也许可以在那里进行修改。
| 归档时间: |
|
| 查看次数: |
94 次 |
| 最近记录: |