Skip to content

Fix #5135: Prevent CondExp from dropping address spaces#5144

Open
anushkagupta200615-jpg wants to merge 10 commits into
ldc-developers:masterfrom
anushkagupta200615-jpg:fix-issue-5135
Open

Fix #5135: Prevent CondExp from dropping address spaces#5144
anushkagupta200615-jpg wants to merge 10 commits into
ldc-developers:masterfrom
anushkagupta200615-jpg:fix-issue-5135

Conversation

@anushkagupta200615-jpg

Copy link
Copy Markdown

This fixes an issue in the DCompute backend where ternary condition expressions dropped device address spaces. R-values now use local allocas for the value, and L-values natively preserve pointer types through LLVM PHI nodes.

Fixes #5135.

This PR fixes a bug in the DCompute backend where ternary condition expressions (c) ? e1 : e2 stripped device address spaces (e.g., addrspace(1)) from GlobalPointer memory references.

Root Cause:
Previously, visit(CondExp) indiscriminately wrapped the branches inside a generic address space temporary (condtmp pointer), which forced DSpecialRefValue::getRVal() to reload the branches using getOpaquePtrType(). This stripped out the inner device address space.

Solution:
The code has been updated to distinctly handle ternary expressions depending on their evaluation context:

  1. L-value context (e->isLvalue()): Entirely bypasses intermediate allocation wrappers and DSpecialRefValue. Instead, it fetches the actual device pointers using makeLValue and naturally preserves their exact LLVM type (including addrspace) by linking them into an LLVM PHINode at the branch merge point.
  2. R-value context (!e->isLvalue()): Creates a standard alloca for the target value type (rather than an alloca of a pointer), evaluates the condition branches as standard R-values, and stores them appropriately. This prevents any inner structs (like GlobalPointer) from having their address spaces stripped from memory.

This fully resolves val = (c) ? input[i] : 0 as well as the related val = (c) ? input[i] : input[j] bug.

This fixes an issue in the DCompute backend where ternary condition expressions dropped device address spaces. R-values now use local allocas for the value, and L-values natively preserve pointer types through LLVM PHI nodes.
@JohanEngelen

Copy link
Copy Markdown
Member

Needs a test case.

This fully resolves val = (c) ? input[i] : 0 as well as the related val = (c) ? input[i] : input[j] bug.

You could use this :)

@gulugulubing gulugulubing left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for working on this fix!

Comment thread gen/toir.cpp Outdated
if (retPtr && u->type->toBasetype()->ty != TY::Tnoreturn) {
LLValue *lval = makeLValue(e->loc, u);
DtoStore(lval, retPtr);
if (u->type->toBasetype()->ty != TY::Tnoreturn) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if (u && u->type->toBasetype()->ty != TY::Tnoreturn) { may fix the ci.

Comment thread gen/toir.cpp Outdated
if (retPtr && v->type->toBasetype()->ty != TY::Tnoreturn) {
LLValue *lval = makeLValue(e->loc, v);
DtoStore(lval, retPtr);
if (v->type->toBasetype()->ty != TY::Tnoreturn) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Similarly, if (v && v->type->toBasetype()->ty != TY::Tnoreturn)

@badnikhil

Copy link
Copy Markdown
Contributor

@anushkagupta200615-jpg Add if (u && ...) / if (v && ...) null checks (per gulugulubing's review) and harden the PHI path.

Comment thread gen/toir.cpp Outdated
Comment on lines +2180 to +2182
phi->addIncoming(u_val ? u_val : llvm::UndefValue::get(ptrType), u_bb);
LLValue *v_inc = v_val ? v_val : llvm::UndefValue::get(ptrType);
if (v_inc->getType() != ptrType) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

With opaque pointers these types only differ by address space, and bitcast across address spaces is invalid (needs addrspacecast). Since preserving the address space is the whole point of this PR, unifying via bitcast looks like it would either assert or drop the space again . what's the intended behavior when the two branches have different address spaces?

anushkagupta200615-jpg and others added 3 commits June 17, 2026 15:34
Replace DtoRVal + DtoStoreZextI8 with DtoAssign, which correctly
handles types that cannot be loaded into a register (e.g. large
structs). The previous approach triggered 'getRVal() for memory-only
type' assertion in DLValue::getRVal().
@gulugulubing

Copy link
Copy Markdown
Contributor

The CI failures are caused by DtoRVal() crashing on memory-only types in the rvalue path. I've submitted a fix PR to your branch: anushkagupta200615-jpg#1. Merging it should solve this problem.

Fix memory-only type crash in rvalue CondExp path
@gulugulubing

gulugulubing commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Hi @anushkagupta200615-jpg, I've been looking into the CI failures(I reported #5135 originally). The rvalue path was straightforward to fix, but the remaining lvalue failure (ternary_lval_assign test) is more subtle than expected.

@badnikhil

badnikhil commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

@gulugulubing the failing test is unsupported for LLVM18 , couldnt reproduce it:) took some effort to figure out the exact problem .. Also i am not very much sure about the side effects(may or may not be) @JohanEngelen @thewilsonator please have a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dcompute: ternary ?: on GlobalPointer load uses generic ptr, reads fail

4 participants