forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WebAssembly] Correctly consider signext/zext arg flags at function d…
…eclaration (llvm#77281) This patch fixes WebAssembly's FastISel pass to correctly consider signext/zeroext parameter flags at function declaration. Previously, the flags at call sites were only considered during code generation, which caused an interesting bug report llvm#63388 . This is problematic especially because in WebAssembly's ABI, either signext or zeroext can be tagged to a function argument, and it must be correctly reflected in the generated code. Unit test https://github.com/llvm/llvm-project/blob/main/llvm/test/CodeGen/WebAssembly/signext-zeroext.ll shows that `i8 zeroext %t` and `i8 signext %t`'s code gen are different.
- Loading branch information
Showing
2 changed files
with
188 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
186 changes: 186 additions & 0 deletions
186
llvm/test/CodeGen/WebAssembly/signext-zeroext-callsite.ll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py | ||
; RUN: llc < %s -O0 | FileCheck %s | ||
; RUN: llc -fast-isel=false < %s -O0 | FileCheck %s -check-prefixes NO-FAST-ISEL | ||
|
||
target triple = "wasm32-unknown-unknown" | ||
|
||
|
||
declare i32 @foo(i1 signext noundef, i32 noundef) | ||
|
||
; callsite_signext and callsite_nosignext must emit equivalent codes | ||
|
||
define i32 @callsite_nosignext() { | ||
; CHECK-LABEL: callsite_nosignext: | ||
; CHECK: .functype callsite_nosignext () -> (i32) | ||
; CHECK-NEXT: .local i32, i32, i32, i32, i32, i32 | ||
; CHECK-NEXT: # %bb.0: # %start | ||
; CHECK-NEXT: i32.const 1 | ||
; CHECK-NEXT: local.set 0 | ||
; CHECK-NEXT: i32.const 0 | ||
; CHECK-NEXT: local.set 1 | ||
; CHECK-NEXT: i32.const 31 | ||
; CHECK-NEXT: local.set 2 | ||
; CHECK-NEXT: local.get 0 | ||
; CHECK-NEXT: local.get 2 | ||
; CHECK-NEXT: i32.shl | ||
; CHECK-NEXT: local.set 3 | ||
; CHECK-NEXT: local.get 3 | ||
; CHECK-NEXT: local.get 2 | ||
; CHECK-NEXT: i32.shr_s | ||
; CHECK-NEXT: local.set 4 | ||
; CHECK-NEXT: local.get 4 | ||
; CHECK-NEXT: local.get 1 | ||
; CHECK-NEXT: call foo | ||
; CHECK-NEXT: local.set 5 | ||
; CHECK-NEXT: local.get 5 | ||
; CHECK-NEXT: return | ||
; | ||
; NO-FAST-ISEL-LABEL: callsite_nosignext: | ||
; NO-FAST-ISEL: .functype callsite_nosignext () -> (i32) | ||
; NO-FAST-ISEL-NEXT: .local i32, i32, i32 | ||
; NO-FAST-ISEL-NEXT: # %bb.0: # %start | ||
; NO-FAST-ISEL-NEXT: i32.const 0 | ||
; NO-FAST-ISEL-NEXT: local.set 0 | ||
; NO-FAST-ISEL-NEXT: i32.const -1 | ||
; NO-FAST-ISEL-NEXT: local.set 1 | ||
; NO-FAST-ISEL-NEXT: local.get 1 | ||
; NO-FAST-ISEL-NEXT: local.get 0 | ||
; NO-FAST-ISEL-NEXT: call foo | ||
; NO-FAST-ISEL-NEXT: local.set 2 | ||
; NO-FAST-ISEL-NEXT: local.get 2 | ||
; NO-FAST-ISEL-NEXT: return | ||
start: | ||
%0 = call i32 @foo(i1 1, i32 0) | ||
ret i32 %0 | ||
} | ||
|
||
define i32 @callsite_signext() { | ||
; CHECK-LABEL: callsite_signext: | ||
; CHECK: .functype callsite_signext () -> (i32) | ||
; CHECK-NEXT: .local i32, i32, i32, i32, i32, i32 | ||
; CHECK-NEXT: # %bb.0: # %start | ||
; CHECK-NEXT: i32.const 1 | ||
; CHECK-NEXT: local.set 0 | ||
; CHECK-NEXT: i32.const 0 | ||
; CHECK-NEXT: local.set 1 | ||
; CHECK-NEXT: i32.const 31 | ||
; CHECK-NEXT: local.set 2 | ||
; CHECK-NEXT: local.get 0 | ||
; CHECK-NEXT: local.get 2 | ||
; CHECK-NEXT: i32.shl | ||
; CHECK-NEXT: local.set 3 | ||
; CHECK-NEXT: local.get 3 | ||
; CHECK-NEXT: local.get 2 | ||
; CHECK-NEXT: i32.shr_s | ||
; CHECK-NEXT: local.set 4 | ||
; CHECK-NEXT: local.get 4 | ||
; CHECK-NEXT: local.get 1 | ||
; CHECK-NEXT: call foo | ||
; CHECK-NEXT: local.set 5 | ||
; CHECK-NEXT: local.get 5 | ||
; CHECK-NEXT: return | ||
; | ||
; NO-FAST-ISEL-LABEL: callsite_signext: | ||
; NO-FAST-ISEL: .functype callsite_signext () -> (i32) | ||
; NO-FAST-ISEL-NEXT: .local i32, i32, i32 | ||
; NO-FAST-ISEL-NEXT: # %bb.0: # %start | ||
; NO-FAST-ISEL-NEXT: i32.const 0 | ||
; NO-FAST-ISEL-NEXT: local.set 0 | ||
; NO-FAST-ISEL-NEXT: i32.const -1 | ||
; NO-FAST-ISEL-NEXT: local.set 1 | ||
; NO-FAST-ISEL-NEXT: local.get 1 | ||
; NO-FAST-ISEL-NEXT: local.get 0 | ||
; NO-FAST-ISEL-NEXT: call foo | ||
; NO-FAST-ISEL-NEXT: local.set 2 | ||
; NO-FAST-ISEL-NEXT: local.get 2 | ||
; NO-FAST-ISEL-NEXT: return | ||
start: | ||
%0 = call i32 @foo(i1 signext 1, i32 0) | ||
ret i32 %0 | ||
} | ||
|
||
declare i32 @foo2(i1 zeroext noundef, i32 noundef) | ||
|
||
; callsite_zeroext and callsite_nozeroext must emit equivalent codes | ||
|
||
define i32 @callsite_nozeroext() { | ||
; CHECK-LABEL: callsite_nozeroext: | ||
; CHECK: .functype callsite_nozeroext () -> (i32) | ||
; CHECK-NEXT: .local i32, i32, i32, i32, i32 | ||
; CHECK-NEXT: # %bb.0: # %start | ||
; CHECK-NEXT: i32.const 1 | ||
; CHECK-NEXT: local.set 0 | ||
; CHECK-NEXT: i32.const 0 | ||
; CHECK-NEXT: local.set 1 | ||
; CHECK-NEXT: i32.const 1 | ||
; CHECK-NEXT: local.set 2 | ||
; CHECK-NEXT: local.get 0 | ||
; CHECK-NEXT: local.get 2 | ||
; CHECK-NEXT: i32.and | ||
; CHECK-NEXT: local.set 3 | ||
; CHECK-NEXT: local.get 3 | ||
; CHECK-NEXT: local.get 1 | ||
; CHECK-NEXT: call foo2 | ||
; CHECK-NEXT: local.set 4 | ||
; CHECK-NEXT: local.get 4 | ||
; CHECK-NEXT: return | ||
; | ||
; NO-FAST-ISEL-LABEL: callsite_nozeroext: | ||
; NO-FAST-ISEL: .functype callsite_nozeroext () -> (i32) | ||
; NO-FAST-ISEL-NEXT: .local i32, i32, i32 | ||
; NO-FAST-ISEL-NEXT: # %bb.0: # %start | ||
; NO-FAST-ISEL-NEXT: i32.const 0 | ||
; NO-FAST-ISEL-NEXT: local.set 0 | ||
; NO-FAST-ISEL-NEXT: i32.const 1 | ||
; NO-FAST-ISEL-NEXT: local.set 1 | ||
; NO-FAST-ISEL-NEXT: local.get 1 | ||
; NO-FAST-ISEL-NEXT: local.get 0 | ||
; NO-FAST-ISEL-NEXT: call foo2 | ||
; NO-FAST-ISEL-NEXT: local.set 2 | ||
; NO-FAST-ISEL-NEXT: local.get 2 | ||
; NO-FAST-ISEL-NEXT: return | ||
start: | ||
%0 = call i32 @foo2(i1 1, i32 0) | ||
ret i32 %0 | ||
} | ||
|
||
define i32 @callsite_zeroext() { | ||
; CHECK-LABEL: callsite_zeroext: | ||
; CHECK: .functype callsite_zeroext () -> (i32) | ||
; CHECK-NEXT: .local i32, i32, i32, i32, i32 | ||
; CHECK-NEXT: # %bb.0: # %start | ||
; CHECK-NEXT: i32.const 1 | ||
; CHECK-NEXT: local.set 0 | ||
; CHECK-NEXT: i32.const 0 | ||
; CHECK-NEXT: local.set 1 | ||
; CHECK-NEXT: i32.const 1 | ||
; CHECK-NEXT: local.set 2 | ||
; CHECK-NEXT: local.get 0 | ||
; CHECK-NEXT: local.get 2 | ||
; CHECK-NEXT: i32.and | ||
; CHECK-NEXT: local.set 3 | ||
; CHECK-NEXT: local.get 3 | ||
; CHECK-NEXT: local.get 1 | ||
; CHECK-NEXT: call foo2 | ||
; CHECK-NEXT: local.set 4 | ||
; CHECK-NEXT: local.get 4 | ||
; CHECK-NEXT: return | ||
; | ||
; NO-FAST-ISEL-LABEL: callsite_zeroext: | ||
; NO-FAST-ISEL: .functype callsite_zeroext () -> (i32) | ||
; NO-FAST-ISEL-NEXT: .local i32, i32, i32 | ||
; NO-FAST-ISEL-NEXT: # %bb.0: # %start | ||
; NO-FAST-ISEL-NEXT: i32.const 0 | ||
; NO-FAST-ISEL-NEXT: local.set 0 | ||
; NO-FAST-ISEL-NEXT: i32.const 1 | ||
; NO-FAST-ISEL-NEXT: local.set 1 | ||
; NO-FAST-ISEL-NEXT: local.get 1 | ||
; NO-FAST-ISEL-NEXT: local.get 0 | ||
; NO-FAST-ISEL-NEXT: call foo2 | ||
; NO-FAST-ISEL-NEXT: local.set 2 | ||
; NO-FAST-ISEL-NEXT: local.get 2 | ||
; NO-FAST-ISEL-NEXT: return | ||
start: | ||
%0 = call i32 @foo2(i1 zeroext 1, i32 0) | ||
ret i32 %0 | ||
} |