Skip to content

Conversation

nikic
Copy link
Contributor

@nikic nikic commented Sep 10, 2025

We consider (in bounds) loads from allocas to always be speculatable, without taking lifetimes into account. This means that such loads cannot be immediate UB. Specify them as returning poison instead.

Due to stack coloring, such a load may end up loading from a different alloca, but that's compatible with poison.

Stores are still UB, but that's a much more narrow problem (I think the only transform violating that part is store scalar promotion in LICM).

Fixes #141892 (and probably a bunch of others...)

We consider (in bounds) loads from allocas to always be speculatable,
without taking lifetimes into account. This means that such loads
cannot be immediate UB. Specify them as returning poison instead.

Due to stack coloring, such a load may end up loading from a
different alloca, but that's compatible with poison.

Stores are still UB, but that's a much more narrow problem
(I think the only transform violating that part is store scalar
promotion in LICM).
@llvmbot
Copy link
Member

llvmbot commented Sep 10, 2025

@llvm/pr-subscribers-llvm-ir

Author: Nikita Popov (nikic)

Changes

We consider (in bounds) loads from allocas to always be speculatable, without taking lifetimes into account. This means that such loads cannot be immediate UB. Specify them as returning poison instead.

Due to stack coloring, such a load may end up loading from a different alloca, but that's compatible with poison.

Stores are still UB, but that's a much more narrow problem (I think the only transform violating that part is store scalar promotion in LICM).

Fixes #141892 (and probably a bunch of others...)


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

1 Files Affected:

  • (modified) llvm/docs/LangRef.rst (+4)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index a06be9dfb9403..3ba94d0d483bf 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -3388,6 +3388,10 @@ the object's lifetime. A stack object's lifetime can be explicitly specified
 using :ref:`llvm.lifetime.start <int_lifestart>` and
 :ref:`llvm.lifetime.end <int_lifeend>` intrinsic function calls.
 
+As an exception to the above, loading from a stack object outside its lifetime
+is not undefined behavior and returns a poison value instead. Storing to it is
+still undefined behavior.
+
 .. _pointeraliasing:
 
 Pointer Aliasing Rules

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

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

LG. I will update llubi later.

@dtcxzyw dtcxzyw requested a review from fhahn September 10, 2025 13:44
@dtcxzyw
Copy link
Member

dtcxzyw commented Sep 10, 2025

@fhahn In #51838 you mentioned that LICM may sink stores out of a loop. Does it guarantee that the store is never executed if the alloca is dead?

@nikic
Copy link
Contributor Author

nikic commented Sep 10, 2025

@dtcxzyw No, this store does unfortunately get executed, resulting in observable miscompiles when stack coloring is enabled. We still need a solution for that case. (Likely by actually checking whether we are within the alloca lifetime or not.)

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

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

I think, in practice, optimizations already worked this way. Shrink-wrapping doesn't track alloca lifetimes, and without shrinkwrapping an alloca is always backed by memory anyway. And we don't have the infrastructure for IR optimizations to prove a load is outside the lifetime of an alloca.

I guess it doesn't hurt to explicitly state it, though.

Copy link
Member

@nunoplopes nunoplopes left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Contributor

@fhahn fhahn left a comment

Choose a reason for hiding this comment

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

LGTM, thanks

@nikic nikic merged commit 9334ef9 into llvm:main Sep 11, 2025
12 checks passed
@nikic nikic deleted the load-before-lifetime branch September 11, 2025 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SimplifyCFG] Speculated load accesses a dead object

6 participants