Skip to content

Conversation

@pcc
Copy link
Contributor

@pcc pcc commented Aug 1, 2025

Protected pointer field loads/stores should be paired with the intrinsic
to avoid unnecessary address escapes.

pcc added 2 commits July 31, 2025 23:18
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1
@pcc pcc requested a review from nikic as a code owner August 1, 2025 06:18
@pcc pcc removed the request for review from nikic August 1, 2025 06:18
@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Aug 1, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 1, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Peter Collingbourne (pcc)

Changes

Protected pointer field loads/stores should be paired with the intrinsic
to avoid unnecessary address escapes.


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

5 Files Affected:

  • (modified) llvm/include/llvm/Transforms/Utils/Local.h (+4)
  • (modified) llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp (+2-4)
  • (modified) llvm/lib/Transforms/Utils/Local.cpp (+1-4)
  • (modified) llvm/lib/Transforms/Utils/SimplifyCFG.cpp (+16)
  • (added) llvm/test/Transforms/Util/phi-protected-field-ptr.ll (+33)
diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h
index 3f5f4278a2766..557f610e10851 100644
--- a/llvm/include/llvm/Transforms/Utils/Local.h
+++ b/llvm/include/llvm/Transforms/Utils/Local.h
@@ -182,6 +182,10 @@ LLVM_ABI bool EliminateDuplicatePHINodes(BasicBlock *BB);
 LLVM_ABI bool EliminateDuplicatePHINodes(BasicBlock *BB,
                                          SmallPtrSetImpl<PHINode *> &ToRemove);
 
+/// Returns whether it is allowed and beneficial for optimizations to transform
+/// phi(load(ptr)) into load(phi(ptr)) or a similar transformation for stores.
+bool shouldFoldLoadStoreWithPointerOperandThroughPhi(const Value *Ptr);
+
 /// This function is used to do simplification of a CFG.  For example, it
 /// adjusts branches to branches to eliminate the extra hop, it eliminates
 /// unreachable basic blocks, and does other peephole optimization of the CFG.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
index 6477141ab095f..f156883d9dcee 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp
@@ -697,8 +697,7 @@ static bool isSafeAndProfitableToSinkLoad(LoadInst *L) {
 Instruction *InstCombinerImpl::foldPHIArgLoadIntoPHI(PHINode &PN) {
   LoadInst *FirstLI = cast<LoadInst>(PN.getIncomingValue(0));
 
-  // Can't forward swifterror through a phi.
-  if (FirstLI->getOperand(0)->isSwiftError())
+  if (!shouldFoldLoadStoreWithPointerOperandThroughPhi(FirstLI->getOperand(0)))
     return nullptr;
 
   // FIXME: This is overconservative; this transform is allowed in some cases
@@ -737,8 +736,7 @@ Instruction *InstCombinerImpl::foldPHIArgLoadIntoPHI(PHINode &PN) {
         LI->getPointerAddressSpace() != LoadAddrSpace)
       return nullptr;
 
-    // Can't forward swifterror through a phi.
-    if (LI->getOperand(0)->isSwiftError())
+    if (!shouldFoldLoadStoreWithPointerOperandThroughPhi(LI->getOperand(0)))
       return nullptr;
 
     // We can't sink the load if the loaded value could be modified between
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index babd7f6b3a058..e2bf56b774493 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -3846,10 +3846,7 @@ bool llvm::canReplaceOperandWithVariable(const Instruction *I, unsigned OpIdx) {
   if (Op->getType()->isMetadataTy())
     return false;
 
-  // swifterror pointers can only be used by a load, store, or as a swifterror
-  // argument; swifterror pointers are not allowed to be used in select or phi
-  // instructions.
-  if (Op->isSwiftError())
+  if (!shouldFoldLoadStoreWithPointerOperandThroughPhi(Op))
     return false;
 
   // Cannot replace alloca argument with phi/select.
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 674de573f7919..6b7a05b1bd57c 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -2134,6 +2134,22 @@ static bool replacingOperandWithVariableIsCheap(const Instruction *I,
   return !isa<IntrinsicInst>(I);
 }
 
+bool llvm::shouldFoldLoadStoreWithPointerOperandThroughPhi(const Value *Ptr) {
+  // swifterror pointers can only be used by a load or store; sinking a load
+  // or store would require introducing a select for the pointer operand,
+  // which isn't allowed for swifterror pointers.
+  if (Ptr->isSwiftError())
+    return false;
+
+  // Protected pointer field loads/stores should be paired with the intrinsic
+  // to avoid unnecessary address escapes.
+  if (auto *II = dyn_cast<IntrinsicInst>(Ptr))
+    if (II->getIntrinsicID() == Intrinsic::protected_field_ptr)
+      return false;
+
+  return true;
+}
+
 // All instructions in Insts belong to different blocks that all unconditionally
 // branch to a common successor. Analyze each instruction and return true if it
 // would be possible to sink them into their successor, creating one common
diff --git a/llvm/test/Transforms/Util/phi-protected-field-ptr.ll b/llvm/test/Transforms/Util/phi-protected-field-ptr.ll
new file mode 100644
index 0000000000000..2c667125edda3
--- /dev/null
+++ b/llvm/test/Transforms/Util/phi-protected-field-ptr.ll
@@ -0,0 +1,33 @@
+; RUN: opt -O2 -S < %s | FileCheck %s
+
+; Test that no optimization run at -O2 moves the loads into the exit block,
+; as this causes unnecessary address escapes with pointer field protection.
+
+target triple = "aarch64-unknown-linux-gnu"
+
+define ptr @phi_prot_ptr(i1 %sel, ptr %p1, ptr %p2) {
+  br i1 %sel, label %t, label %f
+
+; CHECK: t:
+t:
+  ; CHECK-NEXT: call
+  %protp1 = call ptr @llvm.protected.field.ptr(ptr %p1, i64 1, i1 true)
+  ; CHECK-NEXT: load
+  %load1 = load ptr, ptr %protp1
+  br label %exit
+
+; CHECK: f:
+f:
+  ; CHECK-NEXT: call
+  %protp2 = call ptr @llvm.protected.field.ptr(ptr %p2, i64 2, i1 true)
+  ; CHECK-NEXT: load
+  %load2 = load ptr, ptr %protp2
+  br label %exit
+
+; CHECK: exit:
+exit:
+  ; CHECK-NEXT: phi
+  %retval = phi ptr [ %load1, %t ], [ %load2, %f ]
+  ; CHECK-NEXT: ret
+  ret ptr %retval
+}

pcc added a commit to pcc/llvm-project that referenced this pull request Aug 1, 2025
…ld.ptr.

Protected pointer field loads/stores should be paired with the intrinsic
to avoid unnecessary address escapes.

Pull Request: llvm#151649
// argument; swifterror pointers are not allowed to be used in select or phi
// instructions.
if (Op->isSwiftError())
if (!shouldFoldLoadStoreWithPointerOperandThroughPhi(Op))
Copy link
Contributor

Choose a reason for hiding this comment

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

This is going to apply not to just loads and stores.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, gave it a better name.

; Test that no optimization run at -O2 moves the loads into the exit block,
; as this causes unnecessary address escapes with pointer field protection.

target triple = "aarch64-unknown-linux-gnu"
Copy link
Contributor

Choose a reason for hiding this comment

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

Needs to either drop the triple (more likely) or add REQUIRES.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

pcc added 2 commits August 1, 2025 17:28
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1

// Can't forward swifterror through a phi.
if (FirstLI->getOperand(0)->isSwiftError())
if (!shouldFoldOperandThroughPhi(FirstLI->getOperand(0)))
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 my general preference would be to not add a new API for this and just call canReplaceOperandWithVariable() here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, done.

pcc added 4 commits September 2, 2025 18:15
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1
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

pcc added 10 commits September 8, 2025 17:56
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1
pcc added 2 commits November 25, 2025 22:33
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1
@github-actions
Copy link

🐧 Linux x64 Test Results

  • 166334 tests passed
  • 2867 tests skipped
  • 2 tests failed

Failed Tests

(click on a test name to see its output)

LLVM

LLVM.Transforms/PreISelIntrinsicLowering/protected-field-pointer-addrspace1.ll
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 2
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/opt -passes=pre-isel-intrinsic-lowering -S < /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Transforms/PreISelIntrinsicLowering/protected-field-pointer-addrspace1.ll | /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck --check-prefix=NOPAUTH /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Transforms/PreISelIntrinsicLowering/protected-field-pointer-addrspace1.ll
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/opt -passes=pre-isel-intrinsic-lowering -S
# note: command had no output on stdout or stderr
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck --check-prefix=NOPAUTH /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Transforms/PreISelIntrinsicLowering/protected-field-pointer-addrspace1.ll
# .---command stderr------------
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Transforms/PreISelIntrinsicLowering/protected-field-pointer-addrspace1.ll:160:12: error: NOPAUTH: expected string not found in input
# | ; NOPAUTH: attributes #[[ATTR2:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
# |            ^
# | <stdin>:79:42: note: scanning from here
# | attributes #1 = { nounwind memory(none) }
# |                                          ^
# | <stdin>:80:22: note: possible intended match here
# | attributes #2 = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
# |                      ^
# | 
# | Input file: <stdin>
# | Check file: /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Transforms/PreISelIntrinsicLowering/protected-field-pointer-addrspace1.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |              .
# |              .
# |              .
# |             74:  
# |             75: ; Function Attrs: nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) 
# |             76: declare i64 @llvm.fshl.i64(i64, i64, i64) #2 
# |             77:  
# |             78: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(none) } 
# |             79: attributes #1 = { nounwind memory(none) } 
# | check:160'0                                              X error: no match found
# |             80: attributes #2 = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) } 
# | check:160'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# | check:160'1                          ?                                                                                            possible intended match
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

LLVM.Transforms/PreISelIntrinsicLowering/protected-field-pointer.ll
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 2
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/opt -passes=pre-isel-intrinsic-lowering -S < /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Transforms/PreISelIntrinsicLowering/protected-field-pointer.ll | /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck --check-prefix=NOPAUTH /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Transforms/PreISelIntrinsicLowering/protected-field-pointer.ll
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/opt -passes=pre-isel-intrinsic-lowering -S
# note: command had no output on stdout or stderr
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck --check-prefix=NOPAUTH /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Transforms/PreISelIntrinsicLowering/protected-field-pointer.ll
# .---command stderr------------
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Transforms/PreISelIntrinsicLowering/protected-field-pointer.ll:160:12: error: NOPAUTH: expected string not found in input
# | ; NOPAUTH: attributes #[[ATTR2:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
# |            ^
# | <stdin>:79:42: note: scanning from here
# | attributes #1 = { nounwind memory(none) }
# |                                          ^
# | <stdin>:80:22: note: possible intended match here
# | attributes #2 = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
# |                      ^
# | 
# | Input file: <stdin>
# | Check file: /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/test/Transforms/PreISelIntrinsicLowering/protected-field-pointer.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |              .
# |              .
# |              .
# |             74:  
# |             75: ; Function Attrs: nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) 
# |             76: declare i64 @llvm.fshl.i64(i64, i64, i64) #2 
# |             77:  
# |             78: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(none) } 
# |             79: attributes #1 = { nounwind memory(none) } 
# | check:160'0                                              X error: no match found
# |             80: attributes #2 = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) } 
# | check:160'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# | check:160'1                          ?                                                                                            possible intended match
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

pcc added 2 commits December 3, 2025 17:41
Created using spr 1.3.6-beta.1

[skip ci]
Created using spr 1.3.6-beta.1
@pcc pcc changed the base branch from users/pcc/spr/main.utils-inhibit-loadstore-folding-through-phis-for-llvmprotectedfieldptr to main December 4, 2025 01:42
@pcc pcc merged commit e60d62b into main Dec 4, 2025
10 of 17 checks passed
@pcc pcc deleted the users/pcc/spr/utils-inhibit-loadstore-folding-through-phis-for-llvmprotectedfieldptr branch December 4, 2025 01:42
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Dec 4, 2025
…otected.field.ptr.

Protected pointer field loads/stores should be paired with the intrinsic
to avoid unnecessary address escapes.

Reviewers: nikic

Reviewed By: nikic

Pull Request: llvm/llvm-project#151649
kcloudy0717 pushed a commit to kcloudy0717/llvm-project that referenced this pull request Dec 4, 2025
…ld.ptr.

Protected pointer field loads/stores should be paired with the intrinsic
to avoid unnecessary address escapes.

Reviewers: nikic

Reviewed By: nikic

Pull Request: llvm#151649
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms

Projects

Development

Successfully merging this pull request may close these issues.

4 participants