[llvm] [InstCombine] Emit fatal error on void to non-void conversion (PR #96574)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 24 16:03:16 PDT 2024


https://github.com/yozhu created https://github.com/llvm/llvm-project/pull/96574

It is undefined behavior for a call instruction to expect a non-void return
value while the callee returns void. Instead of continuing optimization, we
now emit a fatal error to report such cases.

>From 21ffcfec918f803a298b9d3816911e494b8e1c5c Mon Sep 17 00:00:00 2001
From: YongKang Zhu <yongzhu at fb.com>
Date: Mon, 24 Jun 2024 15:51:22 -0700
Subject: [PATCH] [InstCombine] Emit fatal error on call instruction that need
 a void to non-void return value conversion

---
 .../InstCombine/InstCombineCalls.cpp          | 20 +++++-
 .../X86/Inputs/index-const-prop-cache-foo.ll  |  4 +-
 .../InstCombine/2008-01-06-VoidCast.ll        | 20 ------
 .../WholeProgramDevirt/branch-funnel.ll       | 64 +++++++++----------
 4 files changed, 51 insertions(+), 57 deletions(-)
 delete mode 100644 llvm/test/Transforms/InstCombine/2008-01-06-VoidCast.ll

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 436cdbff75669..b15289d55bf17 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -72,6 +72,7 @@
 #include <cassert>
 #include <cstdint>
 #include <optional>
+#include <sstream>
 #include <utility>
 #include <vector>
 
@@ -4020,10 +4021,23 @@ bool InstCombinerImpl::transformConstExprCastCall(CallBase &Call) {
       if (Callee->isDeclaration())
         return false;   // Cannot transform this return value.
 
-      if (!Caller->use_empty() &&
-          // void -> non-void is handled specially
-          !NewRetTy->isVoidTy())
+      if (!Caller->use_empty()) {
+        if (NewRetTy->isVoidTy()) {
+          DebugLoc DL = Caller->getDebugLoc();
+          const DILocation *DIL = DL;
+          std::ostringstream ErrMsg;
+          if (DIL)
+            ErrMsg << DIL->getFilename().str() << ":" << DIL->getLine();
+          else
+            ErrMsg << Caller->getFunction()->getName().str();
+          ErrMsg
+              << ": contains a call to " << Callee->getName().str()
+              << ", where a non-void return value is expected but the callee "
+                 "returns void\n";
+          report_fatal_error(StringRef(ErrMsg.str()));
+        }
         return false;   // Cannot transform this return value.
+      }
     }
 
     if (!CallerPAL.isEmpty() && !Caller->use_empty()) {
diff --git a/llvm/test/ThinLTO/X86/Inputs/index-const-prop-cache-foo.ll b/llvm/test/ThinLTO/X86/Inputs/index-const-prop-cache-foo.ll
index 58fb47f947c55..172a42a7b5b35 100644
--- a/llvm/test/ThinLTO/X86/Inputs/index-const-prop-cache-foo.ll
+++ b/llvm/test/ThinLTO/X86/Inputs/index-const-prop-cache-foo.ll
@@ -10,10 +10,10 @@ define i32 @foo() local_unnamed_addr {
 }
 
 ; Function Attrs: nounwind ssp uwtable
-define void @bar() local_unnamed_addr {
+define i32 @bar() local_unnamed_addr {
   %1 = tail call i32 @rand()
   store i32 %1, ptr @gFoo, align 4
-  ret void
+  ret i32 %1
 }
 
 declare i32 @rand() local_unnamed_addr
diff --git a/llvm/test/Transforms/InstCombine/2008-01-06-VoidCast.ll b/llvm/test/Transforms/InstCombine/2008-01-06-VoidCast.ll
deleted file mode 100644
index 2fbdd802b46b8..0000000000000
--- a/llvm/test/Transforms/InstCombine/2008-01-06-VoidCast.ll
+++ /dev/null
@@ -1,20 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
-; RUN: opt < %s -passes=instcombine -S | FileCheck %s
-
-define void @f(i16 %y) {
-; CHECK-LABEL: define {{[^@]+}}@f
-; CHECK-SAME: (i16 [[Y:%.*]]) {
-; CHECK-NEXT:    ret void
-;
-  ret void
-}
-
-define i32 @g(i32 %y) {
-; CHECK-LABEL: define {{[^@]+}}@g
-; CHECK-SAME: (i32 [[Y:%.*]]) {
-; CHECK-NEXT:    [[X:%.*]] = call i32 @f(i32 [[Y]])
-; CHECK-NEXT:    ret i32 [[X]]
-;
-  %x = call i32 @f( i32 %y )
-  ret i32 %x
-}
diff --git a/llvm/test/Transforms/WholeProgramDevirt/branch-funnel.ll b/llvm/test/Transforms/WholeProgramDevirt/branch-funnel.ll
index 0b1023eee2732..fd339f5f58b25 100644
--- a/llvm/test/Transforms/WholeProgramDevirt/branch-funnel.ll
+++ b/llvm/test/Transforms/WholeProgramDevirt/branch-funnel.ll
@@ -157,30 +157,30 @@ declare ptr @llvm.load.relative.i32(ptr, i32)
 @vt4_2_rv = constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vf4_2 to i64), i64 ptrtoint (ptr @vt4_2_rv to i64)) to i32)], !type !8
 
 
-; CHECK-LABEL: define i32 @fn1
+; CHECK-LABEL: define noundef i32 @fn1
 ; CHECK-NOT: call void (...) @llvm.icall.branch.funnel
-define i32 @fn1(ptr %obj) #0 {
+define noundef i32 @fn1(ptr %obj) #0 {
   %vtable = load ptr, ptr %obj
   %p = call i1 @llvm.type.test(ptr %vtable, metadata !"typeid1")
   call void @llvm.assume(i1 %p)
   %fptr = load ptr, ptr %vtable
   ; RETP: call i32 @__typeid_typeid1_0_branch_funnel(ptr nest %vtable, ptr %obj, i32 1)
-  %result = call i32 %fptr(ptr %obj, i32 1)
+  call i32 %fptr(ptr %obj, i32 1)
   ; NORETP: call i32 %
-  ret i32 %result
+  ret i32 0
 }
 
-; CHECK-LABEL: define i32 @fn1_rv
+; CHECK-LABEL: define noundef i32 @fn1_rv
 ; CHECK-NOT: call void (...) @llvm.icall.branch.funnel
-define i32 @fn1_rv(ptr %obj) #0 {
+define noundef i32 @fn1_rv(ptr %obj) #0 {
   %vtable = load ptr, ptr %obj
   %p = call i1 @llvm.type.test(ptr %vtable, metadata !"typeid1_rv")
   call void @llvm.assume(i1 %p)
   %fptr = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
   ; RETP: call i32 @__typeid_typeid1_rv_0_branch_funnel(ptr nest %vtable, ptr %obj, i32 1)
-  %result = call i32 %fptr(ptr %obj, i32 1)
+  call i32 %fptr(ptr %obj, i32 1)
   ; NORETP: call i32 %
-  ret i32 %result
+  ret i32 0
 }
 
 ; CHECK-LABEL: define i32 @fn2
@@ -207,78 +207,78 @@ define i32 @fn2_rv(ptr %obj) #0 {
   ret i32 %result
 }
 
-; CHECK-LABEL: define i32 @fn3
+; CHECK-LABEL: define noundef i32 @fn3
 ; CHECK-NOT: call void (...) @llvm.icall.branch.funnel
-define i32 @fn3(ptr %obj) #0 {
+define noundef i32 @fn3(ptr %obj) #0 {
   %vtable = load ptr, ptr %obj
   %p = call i1 @llvm.type.test(ptr %vtable, metadata !4)
   call void @llvm.assume(i1 %p)
   %fptr = load ptr, ptr %vtable
   ; RETP: call i32 @branch_funnel(ptr
   ; NORETP: call i32 %
-  %result = call i32 %fptr(ptr %obj, i32 1)
-  ret i32 %result
+  call i32 %fptr(ptr %obj, i32 1)
+  ret i32 0
 }
 
-; CHECK-LABEL: define i32 @fn3_rv
+; CHECK-LABEL: define noundef i32 @fn3_rv
 ; CHECK-NOT: call void (...) @llvm.icall.branch.funnel
-define i32 @fn3_rv(ptr %obj) #0 {
+define noundef i32 @fn3_rv(ptr %obj) #0 {
   %vtable = load ptr, ptr %obj
   %p = call i1 @llvm.type.test(ptr %vtable, metadata !9)
   call void @llvm.assume(i1 %p)
   %fptr = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
   ; RETP: call i32 @branch_funnel.1(ptr
   ; NORETP: call i32 %
-  %result = call i32 %fptr(ptr %obj, i32 1)
-  ret i32 %result
+  call i32 %fptr(ptr %obj, i32 1)
+  ret i32 0
 }
 
-; CHECK-LABEL: define i32 @fn4
+; CHECK-LABEL: define noundef i32 @fn4
 ; CHECK-NOT: call void (...) @llvm.icall.branch.funnel
-define i32 @fn4(ptr %obj) #0 {
+define noundef i32 @fn4(ptr %obj) #0 {
   %p = call i1 @llvm.type.test(ptr @vt1_1, metadata !"typeid1")
   call void @llvm.assume(i1 %p)
   %fptr = load ptr, ptr @vt1_1
   ; RETP: call i32 @__typeid_typeid1_0_branch_funnel(ptr nest @vt1_1, ptr %obj, i32 1)
-  %result = call i32 %fptr(ptr %obj, i32 1)
+  call i32 %fptr(ptr %obj, i32 1)
   ; NORETP: call i32 %
-  ret i32 %result
+  ret i32 0
 }
 
-; CHECK-LABEL: define i32 @fn4_cpy
+; CHECK-LABEL: define noundef i32 @fn4_cpy
 ; CHECK-NOT: call void (...) @llvm.icall.branch.funnel
-define i32 @fn4_cpy(ptr %obj) #0 {
+define noundef i32 @fn4_cpy(ptr %obj) #0 {
   %p = call i1 @llvm.type.test(ptr @vt1_1, metadata !"typeid1")
   call void @llvm.assume(i1 %p)
   %fptr = load ptr, ptr @vt1_1
   ; RETP: call i32 @__typeid_typeid1_0_branch_funnel(ptr nest @vt1_1, ptr %obj, i32 1)
-  %result = call i32 %fptr(ptr %obj, i32 1)
+  call i32 %fptr(ptr %obj, i32 1)
   ; NORETP: call i32 %
-  ret i32 %result
+  ret i32 0
 }
 
-; CHECK-LABEL: define i32 @fn4_rv
+; CHECK-LABEL: define noundef i32 @fn4_rv
 ; CHECK-NOT: call void (...) @llvm.icall.branch.funnel
-define i32 @fn4_rv(ptr %obj) #0 {
+define noundef i32 @fn4_rv(ptr %obj) #0 {
   %p = call i1 @llvm.type.test(ptr @vt1_1_rv, metadata !"typeid1_rv")
   call void @llvm.assume(i1 %p)
   %fptr = call ptr @llvm.load.relative.i32(ptr @vt1_1_rv, i32 0)
   ; RETP: call i32 @__typeid_typeid1_rv_0_branch_funnel(ptr nest @vt1_1_rv, ptr %obj, i32 1)
-  %result = call i32 %fptr(ptr %obj, i32 1)
+  call i32 %fptr(ptr %obj, i32 1)
   ; NORETP: call i32 %
-  ret i32 %result
+  ret i32 0
 }
 
-; CHECK-LABEL: define i32 @fn4_rv_cpy
+; CHECK-LABEL: define noundef i32 @fn4_rv_cpy
 ; CHECK-NOT: call void (...) @llvm.icall.branch.funnel
-define i32 @fn4_rv_cpy(ptr %obj) #0 {
+define noundef i32 @fn4_rv_cpy(ptr %obj) #0 {
   %p = call i1 @llvm.type.test(ptr @vt1_1_rv, metadata !"typeid1_rv")
   call void @llvm.assume(i1 %p)
   %fptr = call ptr @llvm.load.relative.i32(ptr @vt1_1_rv, i32 0)
   ; RETP: call i32 @__typeid_typeid1_rv_0_branch_funnel(ptr nest @vt1_1_rv, ptr %obj, i32 1)
-  %result = call i32 %fptr(ptr %obj, i32 1)
+  call i32 %fptr(ptr %obj, i32 1)
   ; NORETP: call i32 %
-  ret i32 %result
+  ret i32 0
 }
 
 ; CHECK-LABEL: define hidden void @__typeid_typeid1_0_branch_funnel(ptr nest %0, ...)



More information about the llvm-commits mailing list