-
Notifications
You must be signed in to change notification settings - Fork 11.3k
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] Correctly consider signext/zext arg flags at function declaration #77281
Conversation
@@ -839,9 +839,11 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) { | |||
|
|||
unsigned Reg; | |||
|
|||
if (Attrs.hasParamAttr(I, Attribute::SExt)) | |||
if (Attrs.hasParamAttr(I, Attribute::SExt) || | |||
(IsDirect && Func->hasParamAttribute(I, Attribute::SExt))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IsDirect is true if the function call isn't an indirect call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have been too long away from LLVM to LGTM, but I would guess that you would have a similar situation with indirect function calls as well. Perhaps you could test a function that takes a function pointer parameter with sext / zext attributes. But, again, perhaps this comment is off-base.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pointers are (on their way to being) opaque; I forget whether function pointers ever had these parameters attached, but in any case they soon won't, so for indirect calls I think it should just be the instruction.
@llvm/pr-subscribers-backend-webassembly Author: Juneyoung Lee (aqjune) ChangesThis patch fixes WebAssembly's FastISel pass to correctly consider signext/zeroext parameter flags at function declaration. Full diff: https://github.com/llvm/llvm-project/pull/77281.diff 2 Files Affected:
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
index 15dc44a0439573..80159974ecd691 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
@@ -839,9 +839,11 @@ bool WebAssemblyFastISel::selectCall(const Instruction *I) {
unsigned Reg;
- if (Attrs.hasParamAttr(I, Attribute::SExt))
+ if (Attrs.hasParamAttr(I, Attribute::SExt) ||
+ (IsDirect && Func->hasParamAttribute(I, Attribute::SExt)))
Reg = getRegForSignedValue(V);
- else if (Attrs.hasParamAttr(I, Attribute::ZExt))
+ else if (Attrs.hasParamAttr(I, Attribute::ZExt) ||
+ (IsDirect && Func->hasParamAttribute(I, Attribute::ZExt)))
Reg = getRegForUnsignedValue(V);
else
Reg = getRegForValue(V);
diff --git a/llvm/test/CodeGen/WebAssembly/signext-zeroext-callsite.ll b/llvm/test/CodeGen/WebAssembly/signext-zeroext-callsite.ll
new file mode 100644
index 00000000000000..02ca578716dc98
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/signext-zeroext-callsite.ll
@@ -0,0 +1,125 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -O0 | FileCheck %s
+
+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
+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
+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
+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
+start:
+ %0 = call i32 @foo2(i1 zeroext 1, i32 0)
+ ret i32 %0
+}
|
@@ -0,0 +1,125 @@ | |||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py | |||
; RUN: llc < %s -O0 | FileCheck %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a test that covers this behavior for DAG ISel? Maybe it would make sense to add test expectations for both here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the -fast-isel=false case to check the case when the regular DAG ISel path is taken.
In case anyone was wondering how this is handled in SelectionDAG, I believe it's covered by CallLoweringInfo ultimately determining if an arg is sext/zext through CallBase::paramHasAttr, which does indeed check both the callsite and the called function (if it's a direct call of course). Indeed, I think you could just switch to using paramHasAttr in this patch and get the desired behaviour (which seems to be what ARMFastISel does in its Although this does fix a real bug, I'd strongly advise that a bug be filed against whatever code generator produced a call lacking the matching signext/zeroext attribute on the caller. I don't believe that in general LLVM is particularly well tested or robust against such inconsistencies, so it's likely to run into bugs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the fix!
I will be back from office in a few hours and check whether I can use CallBase::paramHasAttr. Could you wait a bit until then? |
Yeah, sorry I missed Alex's suggestion there. There's no hurry. |
I updated it to use the paramHasAttr which passed my test as it supposed to do so. :) Thanks for suggestion. |
I checked that the CI has passed - thanks all! |
…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.
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 #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
andi8 signext %t
's code gen are different.