[llvm] [InstCombine] Special handle `va_copy(dst, src) + va_end(src)` (PR #140250)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Fri May 16 06:26:00 PDT 2025


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/140250

Closes https://github.com/llvm/llvm-project/issues/140215.


>From f55daa8b6a33671a26b6ac60340d8629262a1f03 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 16 May 2025 20:37:32 +0800
Subject: [PATCH 1/2] [InstCombine] Add pre-commit tests. NFC.

---
 llvm/test/Transforms/InstCombine/vararg.ll | 50 +++++++++++++++++-----
 1 file changed, 39 insertions(+), 11 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/vararg.ll b/llvm/test/Transforms/InstCombine/vararg.ll
index ef75b0c91ce27..9709e94442416 100644
--- a/llvm/test/Transforms/InstCombine/vararg.ll
+++ b/llvm/test/Transforms/InstCombine/vararg.ll
@@ -1,17 +1,14 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 
 %struct.__va_list = type { ptr, ptr, ptr, i32, i32 }
 
-declare void @llvm.lifetime.start.p0(i64, ptr nocapture)
-declare void @llvm.lifetime.end.p0(i64, ptr nocapture)
-declare void @llvm.va_start(ptr)
-declare void @llvm.va_end(ptr)
-declare void @llvm.va_copy(ptr, ptr)
-
-define i32 @func(ptr nocapture readnone %fmt, ...) {
-; CHECK-LABEL: @func(
-; CHECK: entry:
-; CHECK-NEXT: ret i32 0
+define void @func(ptr nocapture readnone %fmt, ...) {
+; CHECK-LABEL: define void @func(
+; CHECK-SAME: ptr readnone captures(none) [[FMT:%.*]], ...) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    ret void
+;
 entry:
   %va0 = alloca %struct.__va_list, align 8
   %va1 = alloca %struct.__va_list, align 8
@@ -23,6 +20,37 @@ entry:
   call void @llvm.lifetime.end.p0(i64 32, ptr %va1)
   call void @llvm.va_end(ptr %va0)
   call void @llvm.lifetime.end.p0(i64 32, ptr %va0)
-  ret i32 0
+  ret void
 }
 
+declare void @callee(ptr)
+
+define void @func_destroy_copy_src(ptr nocapture readnone %fmt, ...) {
+; CHECK-LABEL: define void @func_destroy_copy_src(
+; CHECK-SAME: ptr readnone captures(none) [[FMT:%.*]], ...) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[VA0:%.*]] = alloca [[STRUCT___VA_LIST:%.*]], align 8
+; CHECK-NEXT:    [[VA1:%.*]] = alloca [[STRUCT___VA_LIST]], align 8
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[VA0]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[VA1]])
+; CHECK-NEXT:    call void @llvm.va_copy.p0(ptr nonnull [[VA1]], ptr nonnull [[VA0]])
+; CHECK-NEXT:    call void @callee(ptr nonnull [[VA1]])
+; CHECK-NEXT:    call void @llvm.va_end.p0(ptr [[VA1]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[VA1]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[VA0]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %va0 = alloca %struct.__va_list, align 8
+  %va1 = alloca %struct.__va_list, align 8
+  call void @llvm.lifetime.start.p0(i64 32, ptr %va0)
+  call void @llvm.lifetime.start.p0(i64 32, ptr %va1)
+  call void @llvm.va_start(ptr %va0)
+  call void @llvm.va_copy(ptr %va1, ptr %va0)
+  call void @llvm.va_end(ptr %va0)
+  call void @callee(ptr %va1)
+  call void @llvm.va_end(ptr %va1)
+  call void @llvm.lifetime.end.p0(i64 32, ptr %va1)
+  call void @llvm.lifetime.end.p0(i64 32, ptr %va0)
+  ret void
+}

>From fd48c14e347c9d75fd20e65e26ddd8ab8e266e5d Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 16 May 2025 21:15:06 +0800
Subject: [PATCH 2/2] [InstCombine] Special handle `va_copy(dst, src) +
 va_end(src)`

---
 llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp | 9 ++++++---
 llvm/test/Transforms/InstCombine/vararg.ll           | 2 ++
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 3d35bf753c40e..12e08c09ea67d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -816,9 +816,12 @@ removeTriviallyEmptyRange(IntrinsicInst &EndI, InstCombinerImpl &IC,
 }
 
 Instruction *InstCombinerImpl::visitVAEndInst(VAEndInst &I) {
-  removeTriviallyEmptyRange(I, *this, [](const IntrinsicInst &I) {
-    return I.getIntrinsicID() == Intrinsic::vastart ||
-           I.getIntrinsicID() == Intrinsic::vacopy;
+  removeTriviallyEmptyRange(I, *this, [&I](const IntrinsicInst &II) {
+    // Bail out on the case where the source va_list of a va_copy is destroyed
+    // immediately by a follow-up va_end.
+    return II.getIntrinsicID() == Intrinsic::vastart ||
+           (II.getIntrinsicID() == Intrinsic::vacopy &&
+            I.getArgOperand(0) != II.getArgOperand(1));
   });
   return nullptr;
 }
diff --git a/llvm/test/Transforms/InstCombine/vararg.ll b/llvm/test/Transforms/InstCombine/vararg.ll
index 9709e94442416..eb24256cfa9bc 100644
--- a/llvm/test/Transforms/InstCombine/vararg.ll
+++ b/llvm/test/Transforms/InstCombine/vararg.ll
@@ -33,7 +33,9 @@ define void @func_destroy_copy_src(ptr nocapture readnone %fmt, ...) {
 ; CHECK-NEXT:    [[VA1:%.*]] = alloca [[STRUCT___VA_LIST]], align 8
 ; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[VA0]])
 ; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[VA1]])
+; CHECK-NEXT:    call void @llvm.va_start.p0(ptr nonnull [[VA0]])
 ; CHECK-NEXT:    call void @llvm.va_copy.p0(ptr nonnull [[VA1]], ptr nonnull [[VA0]])
+; CHECK-NEXT:    call void @llvm.va_end.p0(ptr [[VA0]])
 ; CHECK-NEXT:    call void @callee(ptr nonnull [[VA1]])
 ; CHECK-NEXT:    call void @llvm.va_end.p0(ptr [[VA1]])
 ; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[VA1]])



More information about the llvm-commits mailing list