Skip to content

Commit 7388b74

Browse files
authored
[WebAssembly] Correctly consider signext/zext arg flags at function declaration (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.
1 parent c2b57a0 commit 7388b74

File tree

2 files changed

+188
-2
lines changed

2 files changed

+188
-2
lines changed

llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -839,9 +839,9 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) {
839839

840840
unsigned Reg;
841841

842-
if (Attrs.hasParamAttr(I, Attribute::SExt))
842+
if (Call->paramHasAttr(I, Attribute::SExt))
843843
Reg = getRegForSignedValue(V);
844-
else if (Attrs.hasParamAttr(I, Attribute::ZExt))
844+
else if (Call->paramHasAttr(I, Attribute::ZExt))
845845
Reg = getRegForUnsignedValue(V);
846846
else
847847
Reg = getRegForValue(V);
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc < %s -O0 | FileCheck %s
3+
; RUN: llc -fast-isel=false < %s -O0 | FileCheck %s -check-prefixes NO-FAST-ISEL
4+
5+
target triple = "wasm32-unknown-unknown"
6+
7+
8+
declare i32 @foo(i1 signext noundef, i32 noundef)
9+
10+
; callsite_signext and callsite_nosignext must emit equivalent codes
11+
12+
define i32 @callsite_nosignext() {
13+
; CHECK-LABEL: callsite_nosignext:
14+
; CHECK: .functype callsite_nosignext () -> (i32)
15+
; CHECK-NEXT: .local i32, i32, i32, i32, i32, i32
16+
; CHECK-NEXT: # %bb.0: # %start
17+
; CHECK-NEXT: i32.const 1
18+
; CHECK-NEXT: local.set 0
19+
; CHECK-NEXT: i32.const 0
20+
; CHECK-NEXT: local.set 1
21+
; CHECK-NEXT: i32.const 31
22+
; CHECK-NEXT: local.set 2
23+
; CHECK-NEXT: local.get 0
24+
; CHECK-NEXT: local.get 2
25+
; CHECK-NEXT: i32.shl
26+
; CHECK-NEXT: local.set 3
27+
; CHECK-NEXT: local.get 3
28+
; CHECK-NEXT: local.get 2
29+
; CHECK-NEXT: i32.shr_s
30+
; CHECK-NEXT: local.set 4
31+
; CHECK-NEXT: local.get 4
32+
; CHECK-NEXT: local.get 1
33+
; CHECK-NEXT: call foo
34+
; CHECK-NEXT: local.set 5
35+
; CHECK-NEXT: local.get 5
36+
; CHECK-NEXT: return
37+
;
38+
; NO-FAST-ISEL-LABEL: callsite_nosignext:
39+
; NO-FAST-ISEL: .functype callsite_nosignext () -> (i32)
40+
; NO-FAST-ISEL-NEXT: .local i32, i32, i32
41+
; NO-FAST-ISEL-NEXT: # %bb.0: # %start
42+
; NO-FAST-ISEL-NEXT: i32.const 0
43+
; NO-FAST-ISEL-NEXT: local.set 0
44+
; NO-FAST-ISEL-NEXT: i32.const -1
45+
; NO-FAST-ISEL-NEXT: local.set 1
46+
; NO-FAST-ISEL-NEXT: local.get 1
47+
; NO-FAST-ISEL-NEXT: local.get 0
48+
; NO-FAST-ISEL-NEXT: call foo
49+
; NO-FAST-ISEL-NEXT: local.set 2
50+
; NO-FAST-ISEL-NEXT: local.get 2
51+
; NO-FAST-ISEL-NEXT: return
52+
start:
53+
%0 = call i32 @foo(i1 1, i32 0)
54+
ret i32 %0
55+
}
56+
57+
define i32 @callsite_signext() {
58+
; CHECK-LABEL: callsite_signext:
59+
; CHECK: .functype callsite_signext () -> (i32)
60+
; CHECK-NEXT: .local i32, i32, i32, i32, i32, i32
61+
; CHECK-NEXT: # %bb.0: # %start
62+
; CHECK-NEXT: i32.const 1
63+
; CHECK-NEXT: local.set 0
64+
; CHECK-NEXT: i32.const 0
65+
; CHECK-NEXT: local.set 1
66+
; CHECK-NEXT: i32.const 31
67+
; CHECK-NEXT: local.set 2
68+
; CHECK-NEXT: local.get 0
69+
; CHECK-NEXT: local.get 2
70+
; CHECK-NEXT: i32.shl
71+
; CHECK-NEXT: local.set 3
72+
; CHECK-NEXT: local.get 3
73+
; CHECK-NEXT: local.get 2
74+
; CHECK-NEXT: i32.shr_s
75+
; CHECK-NEXT: local.set 4
76+
; CHECK-NEXT: local.get 4
77+
; CHECK-NEXT: local.get 1
78+
; CHECK-NEXT: call foo
79+
; CHECK-NEXT: local.set 5
80+
; CHECK-NEXT: local.get 5
81+
; CHECK-NEXT: return
82+
;
83+
; NO-FAST-ISEL-LABEL: callsite_signext:
84+
; NO-FAST-ISEL: .functype callsite_signext () -> (i32)
85+
; NO-FAST-ISEL-NEXT: .local i32, i32, i32
86+
; NO-FAST-ISEL-NEXT: # %bb.0: # %start
87+
; NO-FAST-ISEL-NEXT: i32.const 0
88+
; NO-FAST-ISEL-NEXT: local.set 0
89+
; NO-FAST-ISEL-NEXT: i32.const -1
90+
; NO-FAST-ISEL-NEXT: local.set 1
91+
; NO-FAST-ISEL-NEXT: local.get 1
92+
; NO-FAST-ISEL-NEXT: local.get 0
93+
; NO-FAST-ISEL-NEXT: call foo
94+
; NO-FAST-ISEL-NEXT: local.set 2
95+
; NO-FAST-ISEL-NEXT: local.get 2
96+
; NO-FAST-ISEL-NEXT: return
97+
start:
98+
%0 = call i32 @foo(i1 signext 1, i32 0)
99+
ret i32 %0
100+
}
101+
102+
declare i32 @foo2(i1 zeroext noundef, i32 noundef)
103+
104+
; callsite_zeroext and callsite_nozeroext must emit equivalent codes
105+
106+
define i32 @callsite_nozeroext() {
107+
; CHECK-LABEL: callsite_nozeroext:
108+
; CHECK: .functype callsite_nozeroext () -> (i32)
109+
; CHECK-NEXT: .local i32, i32, i32, i32, i32
110+
; CHECK-NEXT: # %bb.0: # %start
111+
; CHECK-NEXT: i32.const 1
112+
; CHECK-NEXT: local.set 0
113+
; CHECK-NEXT: i32.const 0
114+
; CHECK-NEXT: local.set 1
115+
; CHECK-NEXT: i32.const 1
116+
; CHECK-NEXT: local.set 2
117+
; CHECK-NEXT: local.get 0
118+
; CHECK-NEXT: local.get 2
119+
; CHECK-NEXT: i32.and
120+
; CHECK-NEXT: local.set 3
121+
; CHECK-NEXT: local.get 3
122+
; CHECK-NEXT: local.get 1
123+
; CHECK-NEXT: call foo2
124+
; CHECK-NEXT: local.set 4
125+
; CHECK-NEXT: local.get 4
126+
; CHECK-NEXT: return
127+
;
128+
; NO-FAST-ISEL-LABEL: callsite_nozeroext:
129+
; NO-FAST-ISEL: .functype callsite_nozeroext () -> (i32)
130+
; NO-FAST-ISEL-NEXT: .local i32, i32, i32
131+
; NO-FAST-ISEL-NEXT: # %bb.0: # %start
132+
; NO-FAST-ISEL-NEXT: i32.const 0
133+
; NO-FAST-ISEL-NEXT: local.set 0
134+
; NO-FAST-ISEL-NEXT: i32.const 1
135+
; NO-FAST-ISEL-NEXT: local.set 1
136+
; NO-FAST-ISEL-NEXT: local.get 1
137+
; NO-FAST-ISEL-NEXT: local.get 0
138+
; NO-FAST-ISEL-NEXT: call foo2
139+
; NO-FAST-ISEL-NEXT: local.set 2
140+
; NO-FAST-ISEL-NEXT: local.get 2
141+
; NO-FAST-ISEL-NEXT: return
142+
start:
143+
%0 = call i32 @foo2(i1 1, i32 0)
144+
ret i32 %0
145+
}
146+
147+
define i32 @callsite_zeroext() {
148+
; CHECK-LABEL: callsite_zeroext:
149+
; CHECK: .functype callsite_zeroext () -> (i32)
150+
; CHECK-NEXT: .local i32, i32, i32, i32, i32
151+
; CHECK-NEXT: # %bb.0: # %start
152+
; CHECK-NEXT: i32.const 1
153+
; CHECK-NEXT: local.set 0
154+
; CHECK-NEXT: i32.const 0
155+
; CHECK-NEXT: local.set 1
156+
; CHECK-NEXT: i32.const 1
157+
; CHECK-NEXT: local.set 2
158+
; CHECK-NEXT: local.get 0
159+
; CHECK-NEXT: local.get 2
160+
; CHECK-NEXT: i32.and
161+
; CHECK-NEXT: local.set 3
162+
; CHECK-NEXT: local.get 3
163+
; CHECK-NEXT: local.get 1
164+
; CHECK-NEXT: call foo2
165+
; CHECK-NEXT: local.set 4
166+
; CHECK-NEXT: local.get 4
167+
; CHECK-NEXT: return
168+
;
169+
; NO-FAST-ISEL-LABEL: callsite_zeroext:
170+
; NO-FAST-ISEL: .functype callsite_zeroext () -> (i32)
171+
; NO-FAST-ISEL-NEXT: .local i32, i32, i32
172+
; NO-FAST-ISEL-NEXT: # %bb.0: # %start
173+
; NO-FAST-ISEL-NEXT: i32.const 0
174+
; NO-FAST-ISEL-NEXT: local.set 0
175+
; NO-FAST-ISEL-NEXT: i32.const 1
176+
; NO-FAST-ISEL-NEXT: local.set 1
177+
; NO-FAST-ISEL-NEXT: local.get 1
178+
; NO-FAST-ISEL-NEXT: local.get 0
179+
; NO-FAST-ISEL-NEXT: call foo2
180+
; NO-FAST-ISEL-NEXT: local.set 2
181+
; NO-FAST-ISEL-NEXT: local.get 2
182+
; NO-FAST-ISEL-NEXT: return
183+
start:
184+
%0 = call i32 @foo2(i1 zeroext 1, i32 0)
185+
ret i32 %0
186+
}

0 commit comments

Comments
 (0)