[llvm] [IROutliner] Prevent propagating interrupt attribute (PR #153985)

Sam Elliott via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 18 21:05:01 PDT 2025


https://github.com/lenary updated https://github.com/llvm/llvm-project/pull/153985

>From c00a1a15f4ba286e98a296b033d02d7d5da3e66a Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Sat, 16 Aug 2025 16:28:56 -0700
Subject: [PATCH 1/6] [IROutliner] Prevent propagating interrupt attribute

On RISC-V, the interrupt attribute relates only to the prolog and epilog
of the attributed function (and has specific restrictions on the
function's signature). It does not change how that function calls other
functions, and when outlining, the outlined function must not have this
attribute.
---
 llvm/lib/Transforms/IPO/IROutliner.cpp        |   8 +-
 ...ing-compatible-and-never-transfer-riscv.ll | 298 ++++++++++++++++++
 2 files changed, 305 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll

diff --git a/llvm/lib/Transforms/IPO/IROutliner.cpp b/llvm/lib/Transforms/IPO/IROutliner.cpp
index c57981ae4ca0d..045945b25eeb8 100644
--- a/llvm/lib/Transforms/IPO/IROutliner.cpp
+++ b/llvm/lib/Transforms/IPO/IROutliner.cpp
@@ -2235,8 +2235,14 @@ static void fillOverallFunction(
                    *CurrentGroup.OutlinedFunction, CurrentGroup.EndBBs);
 
   // Transfer the attributes from the function to the new function.
-  for (Attribute A : CurrentOS->ExtractedFunction->getAttributes().getFnAttrs())
+  for (Attribute A : CurrentOS->ExtractedFunction->getAttributes().getFnAttrs()) {
+    // QC-Specific - Begin
+    if (M.getTargetTriple().isRISCV() && A.getKindAsString() == "interrupt")
+      continue;
+    // QC-Specific - End
+
     CurrentGroup.OutlinedFunction->addFnAttr(A);
+  }
 
   // Create a new set of output blocks for the first extracted function.
   DenseMap<Value *, BasicBlock *> NewBBs;
diff --git a/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll b/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
new file mode 100644
index 0000000000000..937ee963f6087
--- /dev/null
+++ b/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
@@ -0,0 +1,298 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-globals --include-generated-funcs
+; RUN: opt -mtriple=riscv32 -S -passes=verify,iroutliner -ir-outlining-no-cost < %s | FileCheck %s
+
+; REQUIRES: riscv-registered-target
+
+; This has two compatible regions based on function attributes.  We have attributes
+; that should never be transferred to the outlined functions:
+; - `interrupt`=*
+
+; On RISC-V, the `interrupt` attribute only applies to the prolog and epilog of
+; the annotated function, and not any functions it calls. If this attribute is
+; preserved, there will be codegen errors because of restrictions on the
+; signatures of `interrupt` attributes.
+
+define void @outline_attrs1() #0 {
+entry:
+  %a = alloca i32, align 4
+  %b = alloca i32, align 4
+  %c = alloca i32, align 4
+  store i32 2, ptr %a, align 4
+  store i32 3, ptr %b, align 4
+  store i32 4, ptr %c, align 4
+  %al = load i32, ptr %a
+  %bl = load i32, ptr %b
+  %cl = load i32, ptr %c
+  ret void
+}
+
+define void @outline_attrs2() #1 {
+entry:
+  %a = alloca i32, align 4
+  %b = alloca i32, align 4
+  %c = alloca i32, align 4
+  store i32 2, ptr %a, align 4
+  store i32 3, ptr %b, align 4
+  store i32 4, ptr %c, align 4
+  %al = load i32, ptr %a
+  %bl = load i32, ptr %b
+  %cl = load i32, ptr %c
+  ret void
+}
+
+define void @outline_attrs3() {
+entry:
+  %a = alloca i32, align 4
+  %b = alloca i32, align 4
+  %c = alloca i32, align 4
+  store i32 2, ptr %a, align 4
+  store i32 3, ptr %b, align 4
+  store i32 4, ptr %c, align 4
+  %al = load i32, ptr %a
+  %bl = load i32, ptr %b
+  %cl = load i32, ptr %c
+  ret void
+}
+
+define void @outline_outputs1() #0 {
+entry:
+  %output = alloca i32, align 4
+  %result = alloca i32, align 4
+  %output2 = alloca i32, align 4
+  %result2 = alloca i32, align 4
+  %a = alloca i32, align 4
+  %b = alloca i32, align 4
+  br label %block_2
+block_1:
+  %a2 = alloca i32, align 4
+  %b2 = alloca i32, align 4
+  br label %block_2
+block_2:
+  %a2val = load i32, ptr %a
+  %b2val = load i32, ptr %b
+  %add2 = add i32 2, %a2val
+  %mul2 = mul i32 2, %b2val
+  br label %block_5
+block_3:
+  %aval = load i32, ptr %a
+  %bval = load i32, ptr %b
+  %add = add i32 2, %aval
+  %mul = mul i32 2, %bval
+  br label %block_4
+block_4:
+  store i32 %add, ptr %output, align 4
+  store i32 %mul, ptr %result, align 4
+  br label %block_6
+block_5:
+  store i32 %add2, ptr %output, align 4
+  store i32 %mul2, ptr %result, align 4
+  br label %block_7
+block_6:
+  ret void
+block_7:
+  ret void
+}
+
+define void @outline_outputs2() #1 {
+entry:
+  %output = alloca i32, align 4
+  %result = alloca i32, align 4
+  %output2 = alloca i32, align 4
+  %result2 = alloca i32, align 4
+  %a = alloca i32, align 4
+  %b = alloca i32, align 4
+  br label %block_2
+block_1:
+  %a2 = alloca i32, align 4
+  %b2 = alloca i32, align 4
+  br label %block_2
+block_2:
+  %a2val = load i32, ptr %a
+  %b2val = load i32, ptr %b
+  %add2 = add i32 2, %a2val
+  %mul2 = mul i32 2, %b2val
+  br label %block_5
+block_3:
+  %aval = load i32, ptr %a
+  %bval = load i32, ptr %b
+  %add = add i32 2, %aval
+  %mul = mul i32 2, %bval
+  br label %block_4
+block_4:
+  store i32 %add, ptr %output, align 4
+  store i32 %mul, ptr %result, align 4
+  br label %block_7
+block_5:
+  store i32 %add2, ptr %output, align 4
+  store i32 %mul2, ptr %result, align 4
+  br label %block_6
+block_6:
+  %diff = sub i32 %a2val, %b2val
+  ret void
+block_7:
+  %quot = udiv i32 %add, %mul
+  ret void
+}
+
+attributes #0 = { "interrupt"="machine" }
+attributes #1 = { "interrupt"="qci-nest" }
+; CHECK-LABEL: define {{[^@]+}}@outline_attrs1
+; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = all oca i32, align 4
+; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[C:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @outlined_ir_func_1(ptr [[A]], ptr [[B]], ptr [[C]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@outline_attrs2
+; CHECK-SAME: () #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[C:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @outlined_ir_func_1(ptr [[A]], ptr [[B]], ptr [[C]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@outline_attrs3() {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[C:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    call void @outlined_ir_func_1(ptr [[A]], ptr [[B]], ptr [[C]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@outline_outputs1
+; CHECK-SAME: () #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[OUTPUT:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[RESULT:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[OUTPUT2:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[RESULT2:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    br label [[BLOCK_2:%.*]]
+; CHECK:       block_1:
+; CHECK-NEXT:    [[A2:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B2:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    br label [[BLOCK_2]]
+; CHECK:       block_2:
+; CHECK-NEXT:    [[TMP0:%.*]] = call i1 @outlined_ir_func_0(ptr [[A]], ptr [[B]], ptr [[OUTPUT]], ptr [[RESULT]], ptr null, ptr null, ptr null, ptr null, i32 -1)
+; CHECK-NEXT:    br i1 [[TMP0]], label [[BLOCK_6:%.*]], label [[BLOCK_7:%.*]]
+; CHECK:       block_6:
+; CHECK-NEXT:    ret void
+; CHECK:       block_7:
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@outline_outputs2
+; CHECK-SAME: () #[[ATTR1]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[MUL_LOC:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[ADD_LOC:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B2VAL_LOC:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[A2VAL_LOC:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[OUTPUT:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[RESULT:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[OUTPUT2:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[RESULT2:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    br label [[BLOCK_2:%.*]]
+; CHECK:       block_1:
+; CHECK-NEXT:    [[A2:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B2:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    br label [[BLOCK_2]]
+; CHECK:       block_2:
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 -1, ptr [[A2VAL_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 -1, ptr [[B2VAL_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 -1, ptr [[ADD_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 -1, ptr [[MUL_LOC]])
+; CHECK-NEXT:    [[TMP0:%.*]] = call i1 @outlined_ir_func_0(ptr [[A]], ptr [[B]], ptr [[OUTPUT]], ptr [[RESULT]], ptr [[A2VAL_LOC]], ptr [[B2VAL_LOC]], ptr [[ADD_LOC]], ptr [[MUL_LOC]], i32 0)
+; CHECK-NEXT:    [[A2VAL_RELOAD:%.*]] = load i32, ptr [[A2VAL_LOC]], align 4
+; CHECK-NEXT:    [[B2VAL_RELOAD:%.*]] = load i32, ptr [[B2VAL_LOC]], align 4
+; CHECK-NEXT:    [[ADD_RELOAD:%.*]] = load i32, ptr [[ADD_LOC]], align 4
+; CHECK-NEXT:    [[MUL_RELOAD:%.*]] = load i32, ptr [[MUL_LOC]], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 -1, ptr [[A2VAL_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 -1, ptr [[B2VAL_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 -1, ptr [[ADD_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 -1, ptr [[MUL_LOC]])
+; CHECK-NEXT:    br i1 [[TMP0]], label [[BLOCK_7:%.*]], label [[BLOCK_6:%.*]]
+; CHECK:       block_6:
+; CHECK-NEXT:    [[DIFF:%.*]] = sub i32 [[A2VAL_RELOAD]], [[B2VAL_RELOAD]]
+; CHECK-NEXT:    ret void
+; CHECK:       block_7:
+; CHECK-NEXT:    [[QUOT:%.*]] = udiv i32 [[ADD_RELOAD]], [[MUL_RELOAD]]
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@outlined_ir_func_0
+; CHECK-SAME: (ptr [[TMP0:%.*]], ptr [[TMP1:%.*]], ptr [[TMP2:%.*]], ptr [[TMP3:%.*]], ptr [[TMP4:%.*]], ptr [[TMP5:%.*]], ptr [[TMP6:%.*]], ptr [[TMP7:%.*]], i32 [[TMP8:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:  newFuncRoot:
+; CHECK-NEXT:    br label [[BLOCK_2_TO_OUTLINE:%.*]]
+; CHECK:       block_2_to_outline:
+; CHECK-NEXT:    [[A2VAL:%.*]] = load i32, ptr [[TMP0]], align 4
+; CHECK-NEXT:    [[B2VAL:%.*]] = load i32, ptr [[TMP1]], align 4
+; CHECK-NEXT:    [[ADD2:%.*]] = add i32 2, [[A2VAL]]
+; CHECK-NEXT:    [[MUL2:%.*]] = mul i32 2, [[B2VAL]]
+; CHECK-NEXT:    br label [[BLOCK_5:%.*]]
+; CHECK:       block_3:
+; CHECK-NEXT:    [[AVAL:%.*]] = load i32, ptr [[TMP0]], align 4
+; CHECK-NEXT:    [[BVAL:%.*]] = load i32, ptr [[TMP1]], align 4
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 2, [[AVAL]]
+; CHECK-NEXT:    [[MUL:%.*]] = mul i32 2, [[BVAL]]
+; CHECK-NEXT:    br label [[BLOCK_4:%.*]]
+; CHECK:       block_4:
+; CHECK-NEXT:    store i32 [[ADD]], ptr [[TMP2]], align 4
+; CHECK-NEXT:    store i32 [[MUL]], ptr [[TMP3]], align 4
+; CHECK-NEXT:    br label [[BLOCK_6_EXITSTUB:%.*]]
+; CHECK:       block_5:
+; CHECK-NEXT:    store i32 [[ADD2]], ptr [[TMP2]], align 4
+; CHECK-NEXT:    store i32 [[MUL2]], ptr [[TMP3]], align 4
+; CHECK-NEXT:    br label [[BLOCK_7_EXITSTUB:%.*]]
+; CHECK:       block_6.exitStub:
+; CHECK-NEXT:    switch i32 [[TMP8]], label [[FINAL_BLOCK_1:%.*]] [
+; CHECK-NEXT:      i32 0, label [[OUTPUT_BLOCK_1_1:%.*]]
+; CHECK-NEXT:    ]
+; CHECK:       block_7.exitStub:
+; CHECK-NEXT:    switch i32 [[TMP8]], label [[FINAL_BLOCK_0:%.*]] [
+; CHECK-NEXT:      i32 0, label [[OUTPUT_BLOCK_1_0:%.*]]
+; CHECK-NEXT:    ]
+; CHECK:       output_block_1_0:
+; CHECK-NEXT:    store i32 [[A2VAL]], ptr [[TMP4]], align 4
+; CHECK-NEXT:    store i32 [[B2VAL]], ptr [[TMP5]], align 4
+; CHECK-NEXT:    br label [[FINAL_BLOCK_0]]
+; CHECK:       output_block_1_1:
+; CHECK-NEXT:    store i32 [[ADD]], ptr [[TMP6]], align 4
+; CHECK-NEXT:    store i32 [[MUL]], ptr [[TMP7]], align 4
+; CHECK-NEXT:    br label [[FINAL_BLOCK_1]]
+; CHECK:       final_block_0:
+; CHECK-NEXT:    ret i1 false
+; CHECK:       final_block_1:
+; CHECK-NEXT:    ret i1 true
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@outlined_ir_func_1
+; CHECK-SAME: (ptr [[TMP0:%.*]], ptr [[TMP1:%.*]], ptr [[TMP2:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:  newFuncRoot:
+; CHECK-NEXT:    br label [[ENTRY_TO_OUTLINE:%.*]]
+; CHECK:       entry_to_outline:
+; CHECK-NEXT:    store i32 2, ptr [[TMP0]], align 4
+; CHECK-NEXT:    store i32 3, ptr [[TMP1]], align 4
+; CHECK-NEXT:    store i32 4, ptr [[TMP2]], align 4
+; CHECK-NEXT:    [[AL:%.*]] = load i32, ptr [[TMP0]], align 4
+; CHECK-NEXT:    [[BL:%.*]] = load i32, ptr [[TMP1]], align 4
+; CHECK-NEXT:    [[CL:%.*]] = load i32, ptr [[TMP2]], align 4
+; CHECK-NEXT:    br label [[ENTRY_AFTER_OUTLINE_EXITSTUB:%.*]]
+; CHECK:       entry_after_outline.exitStub:
+; CHECK-NEXT:    ret void
+;
+;.
+; CHECK: attributes #[[ATTR0]] = { "interrupt"="machine" }
+; CHECK: attributes #[[ATTR1]] = { "interrupt"="qci-nest" }
+; CHECK: attributes #[[ATTR2:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+; CHECK: attributes #[[ATTR3]] = { minsize optsize }
+;.

>From dcd8028bdab4711422289e71d9600ccbeefa1c37 Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Sat, 16 Aug 2025 16:50:02 -0700
Subject: [PATCH 2/6] clang-format

---
 llvm/lib/Transforms/IPO/IROutliner.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/IPO/IROutliner.cpp b/llvm/lib/Transforms/IPO/IROutliner.cpp
index 045945b25eeb8..343cf990daf0a 100644
--- a/llvm/lib/Transforms/IPO/IROutliner.cpp
+++ b/llvm/lib/Transforms/IPO/IROutliner.cpp
@@ -2235,7 +2235,8 @@ static void fillOverallFunction(
                    *CurrentGroup.OutlinedFunction, CurrentGroup.EndBBs);
 
   // Transfer the attributes from the function to the new function.
-  for (Attribute A : CurrentOS->ExtractedFunction->getAttributes().getFnAttrs()) {
+  for (Attribute A :
+       CurrentOS->ExtractedFunction->getAttributes().getFnAttrs()) {
     // QC-Specific - Begin
     if (M.getTargetTriple().isRISCV() && A.getKindAsString() == "interrupt")
       continue;

>From f2256465e25a6a5e458748f6aa94eef02f0946b9 Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Sat, 16 Aug 2025 19:29:59 -0700
Subject: [PATCH 3/6] Remove QC comments

---
 llvm/lib/Transforms/IPO/IROutliner.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/IROutliner.cpp b/llvm/lib/Transforms/IPO/IROutliner.cpp
index 343cf990daf0a..3a01b82ecd4e7 100644
--- a/llvm/lib/Transforms/IPO/IROutliner.cpp
+++ b/llvm/lib/Transforms/IPO/IROutliner.cpp
@@ -2237,10 +2237,8 @@ static void fillOverallFunction(
   // Transfer the attributes from the function to the new function.
   for (Attribute A :
        CurrentOS->ExtractedFunction->getAttributes().getFnAttrs()) {
-    // QC-Specific - Begin
     if (M.getTargetTriple().isRISCV() && A.getKindAsString() == "interrupt")
       continue;
-    // QC-Specific - End
 
     CurrentGroup.OutlinedFunction->addFnAttr(A);
   }

>From 1737fbcfabe0d049501f7fa6e683b9d701735d22 Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Sun, 17 Aug 2025 18:03:11 -0700
Subject: [PATCH 4/6] Fix test

---
 .../IROutliner/outlining-compatible-and-never-transfer-riscv.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll b/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
index 937ee963f6087..d07eb8861fdc4 100644
--- a/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
+++ b/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
@@ -139,7 +139,7 @@ attributes #1 = { "interrupt"="qci-nest" }
 ; CHECK-LABEL: define {{[^@]+}}@outline_attrs1
 ; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[A:%.*]] = all oca i32, align 4
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
 ; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
 ; CHECK-NEXT:    [[C:%.*]] = alloca i32, align 4
 ; CHECK-NEXT:    call void @outlined_ir_func_1(ptr [[A]], ptr [[B]], ptr [[C]])

>From c2258eb2d431bd13f38b724ad4812aae4cbd35a3 Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Sun, 17 Aug 2025 19:18:01 -0700
Subject: [PATCH 5/6] Update lifetime calls

---
 ...lining-compatible-and-never-transfer-riscv.ll | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll b/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
index d07eb8861fdc4..989534b70d8bb 100644
--- a/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
+++ b/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
@@ -207,19 +207,19 @@ attributes #1 = { "interrupt"="qci-nest" }
 ; CHECK-NEXT:    [[B2:%.*]] = alloca i32, align 4
 ; CHECK-NEXT:    br label [[BLOCK_2]]
 ; CHECK:       block_2:
-; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 -1, ptr [[A2VAL_LOC]])
-; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 -1, ptr [[B2VAL_LOC]])
-; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 -1, ptr [[ADD_LOC]])
-; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 -1, ptr [[MUL_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[A2VAL_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[B2VAL_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[ADD_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[MUL_LOC]])
 ; CHECK-NEXT:    [[TMP0:%.*]] = call i1 @outlined_ir_func_0(ptr [[A]], ptr [[B]], ptr [[OUTPUT]], ptr [[RESULT]], ptr [[A2VAL_LOC]], ptr [[B2VAL_LOC]], ptr [[ADD_LOC]], ptr [[MUL_LOC]], i32 0)
 ; CHECK-NEXT:    [[A2VAL_RELOAD:%.*]] = load i32, ptr [[A2VAL_LOC]], align 4
 ; CHECK-NEXT:    [[B2VAL_RELOAD:%.*]] = load i32, ptr [[B2VAL_LOC]], align 4
 ; CHECK-NEXT:    [[ADD_RELOAD:%.*]] = load i32, ptr [[ADD_LOC]], align 4
 ; CHECK-NEXT:    [[MUL_RELOAD:%.*]] = load i32, ptr [[MUL_LOC]], align 4
-; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 -1, ptr [[A2VAL_LOC]])
-; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 -1, ptr [[B2VAL_LOC]])
-; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 -1, ptr [[ADD_LOC]])
-; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 -1, ptr [[MUL_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[A2VAL_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[B2VAL_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[ADD_LOC]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[MUL_LOC]])
 ; CHECK-NEXT:    br i1 [[TMP0]], label [[BLOCK_7:%.*]], label [[BLOCK_6:%.*]]
 ; CHECK:       block_6:
 ; CHECK-NEXT:    [[DIFF:%.*]] = sub i32 [[A2VAL_RELOAD]], [[B2VAL_RELOAD]]

>From f34901c5ee9abfdd76edd5116e7ba4ae1b2cf9b9 Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Mon, 18 Aug 2025 21:04:46 -0700
Subject: [PATCH 6/6] Fix Attribute Kind Crash

---
 llvm/lib/Transforms/IPO/IROutliner.cpp                    | 3 ++-
 .../outlining-compatible-and-never-transfer-riscv.ll      | 8 ++++----
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/IROutliner.cpp b/llvm/lib/Transforms/IPO/IROutliner.cpp
index 3a01b82ecd4e7..399ed2fd69e8d 100644
--- a/llvm/lib/Transforms/IPO/IROutliner.cpp
+++ b/llvm/lib/Transforms/IPO/IROutliner.cpp
@@ -2237,7 +2237,8 @@ static void fillOverallFunction(
   // Transfer the attributes from the function to the new function.
   for (Attribute A :
        CurrentOS->ExtractedFunction->getAttributes().getFnAttrs()) {
-    if (M.getTargetTriple().isRISCV() && A.getKindAsString() == "interrupt")
+    if (M.getTargetTriple().isRISCV() && A.isStringAttribute() &&
+        A.getKindAsString() == "interrupt")
       continue;
 
     CurrentGroup.OutlinedFunction->addFnAttr(A);
diff --git a/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll b/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
index 989534b70d8bb..d2751e12f355e 100644
--- a/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
+++ b/llvm/test/Transforms/IROutliner/outlining-compatible-and-never-transfer-riscv.ll
@@ -134,8 +134,8 @@ block_7:
   ret void
 }
 
-attributes #0 = { "interrupt"="machine" }
-attributes #1 = { "interrupt"="qci-nest" }
+attributes #0 = { "interrupt"="machine" optsize }
+attributes #1 = { "interrupt"="qci-nest" optsize }
 ; CHECK-LABEL: define {{[^@]+}}@outline_attrs1
 ; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
 ; CHECK-NEXT:  entry:
@@ -291,8 +291,8 @@ attributes #1 = { "interrupt"="qci-nest" }
 ; CHECK-NEXT:    ret void
 ;
 ;.
-; CHECK: attributes #[[ATTR0]] = { "interrupt"="machine" }
-; CHECK: attributes #[[ATTR1]] = { "interrupt"="qci-nest" }
+; CHECK: attributes #[[ATTR0]] = { optsize "interrupt"="machine" }
+; CHECK: attributes #[[ATTR1]] = { optsize "interrupt"="qci-nest" }
 ; CHECK: attributes #[[ATTR2:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 ; CHECK: attributes #[[ATTR3]] = { minsize optsize }
 ;.



More information about the llvm-commits mailing list