Skip to content

Conversation

@tylanphear
Copy link
Contributor

In SLPVectorizer, when computing spill costs for a tree, we call BasicBlock::getFirstNonPHIOrDbgOrAlloca() without checking if its return value is an end iterator. This can occur if the basic block is a catchswitch block. Dereferencing this iterator in an assertions build with sentinel-tracking causes a crash. This patch commits a test which demonstrates this issue.

In SLPVectorizer, when computing spill costs for a tree, we call
`BasicBlock::getFirstNonPHIOrDbgOrAlloca()` without checking if its
return value is an end iterator. This can occur if the basic block is a
`catchswitch` block. Dereferencing this iterator in an assertions build
with sentinel-tracking causes a crash. This patch commits a test which
demonstrates this issue.
@llvmbot
Copy link
Member

llvmbot commented Apr 17, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Tyler Lanphear (tylanphear)

Changes

In SLPVectorizer, when computing spill costs for a tree, we call BasicBlock::getFirstNonPHIOrDbgOrAlloca() without checking if its return value is an end iterator. This can occur if the basic block is a catchswitch block. Dereferencing this iterator in an assertions build with sentinel-tracking causes a crash. This patch commits a test which demonstrates this issue.


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

1 Files Affected:

  • (added) llvm/test/Transforms/SLPVectorizer/catchswitch2.ll (+72)
diff --git a/llvm/test/Transforms/SLPVectorizer/catchswitch2.ll b/llvm/test/Transforms/SLPVectorizer/catchswitch2.ll
new file mode 100644
index 0000000000000..d6e650aa075df
--- /dev/null
+++ b/llvm/test/Transforms/SLPVectorizer/catchswitch2.ll
@@ -0,0 +1,72 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; REQUIRES: asserts
+; RUN: opt -passes=slp-vectorizer -S < %s | FileCheck %s
+
+; This test demonstrates a crash when trying to compute spill costs when
+; vectorizing a tree in a function which contains a `catchswitch` block.
+
+; XFAIL: *
+
+target triple = "x86_64-pc-windows-msvc19.40.33811"
+
+%"class.boost::execution_exception" = type { i32, %"class.boost::unit_test::basic_cstring", %"struct.boost::execution_exception::location" }
+%"class.boost::unit_test::basic_cstring" = type { ptr, ptr }
+%"struct.boost::execution_exception::location" = type { %"class.boost::unit_test::basic_cstring", i64, %"class.boost::unit_test::basic_cstring" }
+
+define void @foo() personality ptr null {
+; CHECK-LABEL: define void @foo() personality ptr null {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[FOR_COND109:.*]]
+; CHECK:       [[FOR_COND109]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = phi <2 x ptr> [ zeroinitializer, %[[ENTRY]] ], [ zeroinitializer, %[[IF_END153:.*]] ]
+; CHECK-NEXT:    br label %[[INIT_ATTEMPT_I664:.*]]
+; CHECK:       [[INIT_ATTEMPT_I664]]:
+; CHECK-NEXT:    [[CALL_I666:%.*]] = invoke ptr null(ptr null)
+; CHECK-NEXT:            to label %[[INVOKE_CONT_I668:.*]] unwind label %[[CATCH_DISPATCH:.*]]
+; CHECK:       [[INVOKE_CONT_I668]]:
+; CHECK-NEXT:    ret void
+; CHECK:       [[CATCH_DISPATCH]]:
+; CHECK-NEXT:    [[TMP1:%.*]] = catchswitch within none [label %catch] unwind to caller
+; CHECK:       [[CATCH:.*]]:
+; CHECK-NEXT:    [[TMP2:%.*]] = catchpad within [[TMP1]] [ptr null, i32 0, ptr null]
+; CHECK-NEXT:    br i1 false, label %[[IF_END153]], label %[[INVOKE_CONT149:.*]]
+; CHECK:       [[INVOKE_CONT149]]:
+; CHECK-NEXT:    [[TMP3:%.*]] = load <2 x ptr>, ptr null, align 8
+; CHECK-NEXT:    br label %[[IF_END153]]
+; CHECK:       [[IF_END153]]:
+; CHECK-NEXT:    [[TMP4:%.*]] = phi <2 x ptr> [ [[TMP0]], %[[CATCH]] ], [ [[TMP3]], %[[INVOKE_CONT149]] ]
+; CHECK-NEXT:    catchret from [[TMP2]] to label %[[FOR_COND109]]
+;
+entry:
+  br label %for.cond109
+
+for.cond109:                                      ; preds = %if.end153, %entry
+  %setup_error.sroa.0.1 = phi ptr [ null, %entry ], [ null, %if.end153 ]
+  %setup_error.sroa.6.1 = phi ptr [ null, %entry ], [ null, %if.end153 ]
+  br label %init.attempt.i664
+
+init.attempt.i664:                                ; preds = %for.cond109
+  %call.i666 = invoke ptr null(ptr null)
+  to label %invoke.cont.i668 unwind label %catch.dispatch
+
+invoke.cont.i668:                                 ; preds = %init.attempt.i664
+  ret void
+
+catch.dispatch:                                   ; preds = %init.attempt.i664
+  %0 = catchswitch within none [label %catch] unwind to caller
+
+catch:                                            ; preds = %catch.dispatch
+  %1 = catchpad within %0 [ptr null, i32 0, ptr null]
+  br i1 false, label %if.end153, label %invoke.cont149
+
+invoke.cont149:                                   ; preds = %catch
+  %m_begin2.i.i = getelementptr %"class.boost::execution_exception", ptr null, i64 0, i32 1, i32 0
+  %2 = load ptr, ptr %m_begin2.i.i, align 8
+  %3 = load ptr, ptr null, align 8
+  br label %if.end153
+
+if.end153:                                        ; preds = %invoke.cont149, %catch
+  %setup_error.sroa.0.2 = phi ptr [ %setup_error.sroa.0.1, %catch ], [ %2, %invoke.cont149 ]
+  %setup_error.sroa.6.2 = phi ptr [ %setup_error.sroa.6.1, %catch ], [ %3, %invoke.cont149 ]
+  catchret from %1 to label %for.cond109
+}

@alexey-bataev
Copy link
Member

Thanks for the test, I used it for the fix in 5fe91f1

@tylanphear tylanphear closed this Apr 17, 2025
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.

3 participants