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

[InstCombine] Remove transformation on call instruction where return value need void to non-void conversion #98536

Merged
merged 6 commits into from
Aug 2, 2024

Conversation

yozhu
Copy link
Contributor

@yozhu yozhu commented Jul 11, 2024

Skip simplification on call instruction where a non-void return value is
expected but the callee returns void, which is undefined behavior and can
lead to non-determinism runtime behaviors or crashes.

@llvmbot
Copy link
Collaborator

llvmbot commented Jul 11, 2024

@llvm/pr-subscribers-llvm-transforms

Author: None (yozhu)

Changes

Add an option to skip simplification on call instruction where a non-void
return value is expected but the callee returns void, which is undefined
behavior and can lead to non-determinism runtime behaviors or crashes.


Full diff: https://github.com/llvm/llvm-project/pull/98536.diff

1 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp (+6-1)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 0ca56f08c333d..880932159f570 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -89,6 +89,11 @@ static cl::opt<unsigned> GuardWideningWindow(
     cl::desc("How wide an instruction window to bypass looking for "
              "another guard"));
 
+static cl::opt<bool> SkipRetTypeVoidToNonVoidCallInst(
+    "instcombine-skip-call-ret-type-void-to-nonvoid", cl::init(false),
+    cl::desc("skip simplifying call instruction which expects a non-void "
+             "return value but the callee returns void"));
+
 /// Return the specified type promoted as it would be to pass though a va_arg
 /// area.
 static Type *getPromotedType(Type *Ty) {
@@ -4073,7 +4078,7 @@ bool InstCombinerImpl::transformConstExprCastCall(CallBase &Call) {
 
       if (!Caller->use_empty() &&
           // void -> non-void is handled specially
-          !NewRetTy->isVoidTy())
+          (SkipRetTypeVoidToNonVoidCallInst || !NewRetTy->isVoidTy()))
         return false;   // Cannot transform this return value.
     }
 

@yozhu
Copy link
Contributor Author

yozhu commented Jul 11, 2024

Sorry - I messed up my local clone and branches and had to recreate this PR. It was the same code change as in #96574, where the conclusion is that adding an option to disable this optimization is fine but emitting a fatal error is not allowed.

@goldsteinn
Copy link
Contributor

missing tests?

@yozhu yozhu requested a review from goldsteinn July 15, 2024 21:45
@yozhu
Copy link
Contributor Author

yozhu commented Jul 26, 2024

Ping ~~ @nikic @goldsteinn

@nikic
Copy link
Contributor

nikic commented Jul 27, 2024

I think we should remove the void/non-void transform entirely, not just add an option. I don't really see a good reason to perform this transform. Looking back at the history, I think this is just a leftover of an overly liberal initial implementation.

@yozhu
Copy link
Contributor Author

yozhu commented Jul 29, 2024

I think we should remove the void/non-void transform entirely, not just add an option. I don't really see a good reason to perform this transform. Looking back at the history, I think this is just a leftover of an overly liberal initial implementation.

Happy to remove this transformation entirely.

Given this, should we also emit an error when seeing this void to non-void conversion at call sites? It is known to be undefined behavior that could result in non-determinism or crash at runtime; and If it is non-LTO compilation, clang would output an error unconditionally.

@yozhu yozhu changed the title [InstCombine] Add an option to skip simplification on call instruction where a non-void return value is expected while the callee returns void [InstCombine] Remove transformation on call instruction where return value need void to non-void conversion Aug 1, 2024
if (!Caller->use_empty() &&
// void -> non-void is handled specially
!NewRetTy->isVoidTy())
if (!Caller->use_empty() || NewRetTy->isVoidTy())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!Caller->use_empty() || NewRetTy->isVoidTy())
if (!Caller->use_empty())

A more conservative change would be this. In that case we'd still allow changing the return type to void as long as the return value is not used. That may be more consistent with the overall handling?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this makes sense.

; RUN: opt --passes=instcombine %t.bc -S | FileCheck %s

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-none-linux-android21"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop data layout and triple.

Comment on lines 2 to 3
; RUN: llvm-as %s -o %t.bc
; RUN: opt --passes=instcombine %t.bc -S | FileCheck %s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
; RUN: llvm-as %s -o %t.bc
; RUN: opt --passes=instcombine %t.bc -S | FileCheck %s
; RUN: opt --passes=instcombine -S < %s | FileCheck %s

; CHECK-NEXT: ret i32 [[TMP1]]
;
entry:
%_rc_ = alloca i32, align 4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is more complicated than it needs to be. You shouldn't need much more here than a call + return.

@yozhu yozhu requested a review from nikic August 1, 2024 16:24
@@ -4233,9 +4231,6 @@ bool InstCombinerImpl::transformConstExprCastCall(CallBase &Call) {

AttributeSet FnAttrs = CallerPAL.getFnAttrs();

if (NewRetTy->isVoidTy())
Caller->setName(""); // Void type should not have a name.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this bit might still be needed? Can you please add another function to your test that does %res = call i32 @foo() but does not use the result? Just want to make sure this doesn't hit some void name assertion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this would necessary now after we have tightened the condition above to skip transformation only when return value has uses.

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt --passes=instcombine -S < %s | FileCheck %s

define dso_local void @foo() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop dso_local.

;
entry:
%1 = tail call i32 @foo()
%2 = add i32 %1, 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can drop the add too :)

@yozhu yozhu requested a review from nikic August 2, 2024 01:04
Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@yozhu yozhu merged commit 6c36716 into llvm:main Aug 2, 2024
5 of 7 checks passed
@yozhu yozhu deleted the instcombinecall branch August 2, 2024 16:08
@AreaZR
Copy link
Contributor

AreaZR commented Aug 2, 2024

Would it be wise to backport this as this is the removal of something undesirable from the codebase? Or are we okay with 19.x retaining this behavior?

@yozhu
Copy link
Contributor Author

yozhu commented Aug 2, 2024

Would it be wise to backport this as this is the removal of something undesirable from the codebase? Or are we okay with 19.x retaining this behavior?

I'm fine with either way. This transformation is not desirable, but it had been in the codebase for quite a long time.

banach-space pushed a commit to banach-space/llvm-project that referenced this pull request Aug 7, 2024
…value need void to non-void conversion (llvm#98536)

Skip simplification on call instruction where a non-void return value is
expected but the callee returns void, which is undefined behavior and
could lead to non-determinism or crashes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants