Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WebAssembly] Problems of forcing mem2reg in reference-types #81575

Closed
aheejin opened this issue Feb 13, 2024 · 8 comments · Fixed by #83196
Closed

[WebAssembly] Problems of forcing mem2reg in reference-types #81575

aheejin opened this issue Feb 13, 2024 · 8 comments · Fixed by #83196

Comments

@aheejin
Copy link
Member

aheejin commented Feb 13, 2024

I recently noticed that we force mem2reg optimization to run unconditionally, even in -O0, when reference-types feature is enabled:

if (Subtarget->hasReferenceTypes()) {
// We need to remove allocas for reference types
addPass(createPromoteMemoryToRegisterPass(true));
}

This was added in 890146b.

I think this can have some problems:

  1. This means we run some optimizations even when we are not supposed to. We will handle debug value differently and debug info can be lost even in -O0, if reference-types is enabled. And the newest EH spec requires reference-types to be enabled, so this means enabling EH means running some optimizations even in -O0. Also, as it happens, we've been even discussing enabling reference-types by default. ([WebAssembly] Enable multivalue and reference-types in generic CPU config #80923) It'd be good if there's a way to promote only reference type values to registers.

  2. When Wasm EH is used, we demote certain PHIs from registers to memory in WinEHPrepare. (Wasm EH uses WinEHPrepare pass, because it uses WinEH IR) Specifically, we cannot have a PHI before a catchswitch. PHIs in catchswitch BBs have to be removed (= demoted) because catchswitchs are removed in ISel and catchswitch BBs are removed as well, so they can't have other instructions. But when reference-types is enabled, we force to run mem2reg in addISelPrepare, which runs after WinEHPrepare, re-promoting the demoted PHIs in WinEHPrepare, which crashes the compiler. This is a problem even if we have a way to promote only reference types because they can be before a catchswitch too.

Any thoughts or ideas?
cc @pmatos

@llvmbot
Copy link
Collaborator

llvmbot commented Feb 13, 2024

@llvm/issue-subscribers-backend-webassembly

Author: Heejin Ahn (aheejin)

I recently noticed that we force mem2reg optimization to run unconditionally, even in `-O0`, when reference-types feature is enabled: https://github.com/llvm/llvm-project/blob/2dd52046816ac706a25d588efac77705793d8778/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp#L479-L482 This was added in https://github.com/llvm/llvm-project/commit/890146b19206827bc48ee1ae1dc1534ff2ff18d7.

I think this can have some problems:

  1. This means we run some optimizations even when we are not supposed to. We will handle debug value differently and debug info can be lost even in -O0, if reference-types is enabled. And the newest EH spec requires reference-types to be enabled, so this means enabling EH means running some optimizations even in -O0. Also, as it happens, we've been even discussing enabling reference-types by default. (#80923) It'd be good if there's a way to promote only reference type values to registers.

  2. When Wasm EH is used, we demote certain PHIs from registers to memory in WinEHPrepare. (Wasm EH uses WinEHPrepare pass, because it uses WinEH IR) Specifically, we cannot have a PHI before a catchswitch. PHIs in catchswitch BBs have to be removed (= demoted) because catchswitchs are removed in ISel and catchswitch BBs are removed as well, so they can't have other instructions. But when reference-types is enabled, we force to run mem2reg in addISelPrepare, which runs after WinEHPrepare, re-promoting the demoted PHIs in WinEHPrepare, which crashes the compiler. This is a problem even if we have a way to promote only reference types because they can be before a catchswitch too.

Any thoughts or ideas?
cc @pmatos

@pmatos
Copy link
Contributor

pmatos commented Feb 13, 2024

@aheejin As it's in the comment we need to promote allocas for reftypes to registers and at the time mem2reg did that for us and it worked fine.

It seems that this causes issues with EH as you say. I think we might need a pass here dedicated to just promoting reftypes to allocas that's slightly more constrained - sort of a specialized mem2reg for reftypes.

@xortoast
Copy link
Contributor

@pmatos Do we need to promote allocas to registers? I think promoting them to locals should be enough, which can be done by moving them from WASM_ADDRESS_SPACE_DEFAULT to WASM_ADDRESS_SPACE_VAR. This would also solve the issues with EH.

@pmatos
Copy link
Contributor

pmatos commented Feb 14, 2024

@pmatos Do we need to promote allocas to registers? I think promoting them to locals should be enough, which can be done by moving them from WASM_ADDRESS_SPACE_DEFAULT to WASM_ADDRESS_SPACE_VAR. This would also solve the issues with EH.

It has been over a year since I looked into this but if memory serves me right, I don't think we have a way to do that in IR atm.

@xortoast
Copy link
Contributor

I don't think we have a way to do that in IR atm.

The alloca instruction accepts an addrspace attribute, and addrspace(1) can be used to lower them to local variables (added in 82f92e3).

The only thing needed would be a new mem2local pass that adds the addrspace(1) to promotable allocas.

@aheejin
Copy link
Member Author

aheejin commented Feb 15, 2024

@xortoast Thanks for the info! Currently it looks Wasm reference types are represented by ptrs to addrspace(10) (for externref) or addrspace(20) (for funcref). If they become ptrs to addrspace(1), their original addrspace info will be lost. Then do you have any suggestions on how we can encode values' type info (externref or funcref) after they are moved to addrspace(1)?

@xortoast
Copy link
Contributor

@aheejin I think you misunderstood a bit. Reference types are represented as ptr addrspace(10) or ptr addrspace(20), but the alloca itself lives in another address space.

For example, a local variable of type externref currently compiles to %1 = alloca ptr addrspace(10), align 1, where %1 has type ptr.

After running mem2local, the same alloca becomes %1 = alloca ptr addrspace(10), align 1, addrspace(1), where %1 has type ptr addrspace(1).

aheejin added a commit to aheejin/llvm-project that referenced this issue Feb 16, 2024
This adds `WebAssemblyRefTypeMem2Local` pass, which changes the address
spaces of reference type `alloca`s to `addrspace(1)`. This in turn
changes the address spaces of all `load` and `store` instructions that
use the `alloca`s.

`addrspace(1)` is `WASM_ADDRESS_SPACE_VAR`, and loads and stores to this
address space become `local.get`s and `local.set`s, thanks to the Wasm
local IR support added in
llvm@82f92e3.

In a follow-up PR, I am planning to replace the usage of mem2reg pass
with this to solve the reference type `alloca` problems described in
 llvm#81575.
@aheejin
Copy link
Member Author

aheejin commented Feb 16, 2024

@xortoast Thanks! Added that pass in #81965.

aheejin added a commit that referenced this issue Feb 27, 2024
This adds `WebAssemblyRefTypeMem2Local` pass, which changes the address
spaces of reference type `alloca`s to `addrspace(1)`. This in turn
changes the address spaces of all `load` and `store` instructions that
use the `alloca`s.

`addrspace(1)` is `WASM_ADDRESS_SPACE_VAR`, and loads and stores to this
address space become `local.get`s and `local.set`s, thanks to the Wasm
local IR support added in

82f92e3.

In a follow-up PR, I am planning to replace the usage of mem2reg pass
with this to solve the reference type `alloca` problems described in
#81575.
aheejin added a commit to aheejin/llvm-project that referenced this issue Feb 27, 2024
When reference-types feature is enabled, forcing mem2reg unconditionally
even in `-O0` has some problems described in llvm#81575. This uses
RefTypeMem2Local pass added in llvm#81965 instead. This also removes
`IsForced` parameter added in
llvm@890146b
given that we don't need it anymore.

This may still hurt debug info related to reference type variables a
little during the backend transformation given that they are not stored
in memory anymore, but reference type variables are presumably rare and
it would be still a lot less damage than forcing mem2reg on the whole
program. Also this fixes the EH problem described in llvm#81575.

Fixes llvm#81575.
aheejin added a commit that referenced this issue Mar 6, 2024
When reference-types feature is enabled, forcing mem2reg unconditionally
even in `-O0` has some problems described in #81575. This uses
RefTypeMem2Local pass added in #81965 instead. This also removes
`IsForced` parameter added in

890146b
given that we don't need it anymore.

This may still hurt debug info related to reference type variables a
little during the backend transformation given that they are not stored
in memory anymore, but reference type variables are presumably rare and
it would be still a lot less damage than forcing mem2reg on the whole
program. Also this fixes the EH problem described in #81575.

Fixes #81575.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants