[clang] [OpenMP] Support capturing structured bindings in OpenMP regions. (PR #190832)
Zahira Ammarguellat via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 10 06:43:56 PDT 2026
================
@@ -3595,6 +3595,75 @@ static bool canEmitSpuriousReferenceToVariable(CodeGenFunction &CGF,
}
}
+/// Emit an LValue for a structured binding captured in an OpenMP region.
+/// Handles extracting individual bindings from the captured decomposed
+/// declaration (struct fields, array elements, etc.).
+LValue CodeGenFunction::EmitOMPCapturedBindingLValue(const BindingDecl *BD) {
+ assert(CapturedStmtInfo && "Expected to be inside a captured region");
+ assert(CapturedStmtInfo->getKind() == CapturedRegionKind::CR_OpenMP &&
+ "Expected OpenMP captured region");
+ assert(CGM.getLangOpts().OpenMP && "Expected OpenMP to be enabled");
+
+ if (auto It = LocalDeclMap.find(BD->getCanonicalDecl());
+ It != LocalDeclMap.end())
+ return MakeAddrLValue(It->second, BD->getType());
+
+ const auto *DD = cast<VarDecl>(BD->getDecomposedDecl());
+
+ // Check if the original variable (what DD decomposes) has been mapped.
+ // If so, use the original variable instead of DD to avoid capturing DD.
+ const VarDecl *TargetDecl = DD;
+ if (const auto *DecompDecl = dyn_cast<DecompositionDecl>(DD)) {
+ if (const VarDecl *OrigVar = DecompDecl->getOriginalVar().Var) {
+ auto It = LocalDeclMap.find(OrigVar->getCanonicalDecl());
+ if (It != LocalDeclMap.end())
+ // Original variable is mapped, use it instead.
+ TargetDecl = OrigVar;
+ }
+ }
+
+ // Use getNonReferenceType() because we need the actual object type, not the
+ // reference type. DeclRefExpr with VK_LValue requires a non-reference type
+ // (AST invariant). EmitDeclRefLValue will load any reference for us.
+ QualType DREType = TargetDecl->getType().getNonReferenceType();
+ DeclRefExpr DRE(getContext(), const_cast<VarDecl *>(TargetDecl),
+ /*RefersToEnclosingVariableOrCapture=*/true, DREType,
+ VK_LValue, SourceLocation());
+ LValue BaseLVal = EmitDeclRefLValue(&DRE);
+
+ // Ensure the Address has the correct element type for DD's type.
+ // EmitDeclRefLValue might return an address with a different element type
+ // if TargetDecl != DD or if reference unwrapping occurred.
+ Address BaseAddr = BaseLVal.getAddress();
+ QualType DDType = DD->getType();
+ llvm::Type *ExpectedTy = CGM.getTypes().ConvertTypeForMem(DDType);
+ if (BaseAddr.getElementType() != ExpectedTy)
+ BaseAddr = BaseAddr.withElementType(ExpectedTy);
----------------
zahiraam wrote:
Removing these lines resulted in a crash in` CreateStructGEP` for `test_target_parallel` in `structured-binding-codegen.cpp`:
Step-by-step failure without the element type adjustment:
1. Line 3647: `EmitDeclRefLValueWithElementType(TargetDecl, DD->getType())`
- `TargetDecl` may be the original variable (from `getOriginalVar()`)
- In target regions, captured parameters have LLVM representation that may not preserve the precise struct type
- Without element type adjustment: `Address` has incorrect element type (not `%struct.Point`)
2. Line 3666: `EmitLValue(BindingExpr)` where `BindingExpr` accesses field `x`
- Calls `EmitLValueForField` -> `emitAddrOfFieldStorage` -> `CreateStructGEP`
3. Line 231 in `CGBuilder.h: CreateStructGEP` does:
`cast<StructType>(Addr.getElementType())` // <- crashes here.
- Tries to cast element type to `StructType`
- Element type is not `%struct.Point` (may be opaque pointer or different representation)
- Assertion fails: "cast() argument of incompatible type!"
The `withElementType()` call only updates LLVM type metadata - it doesn't emit any code or change the pointer value. We need it because when accessing the decomposition through `OrigVar` in target regions, the address has the correct memory layout but may lack precise type information due to how target region captures work. We're correcting the LLVM type metadata to match the actual struct type so `CreateStructGEP` succeeds. The underlying memory is correct. We are just restoring the type annotation.
Alternatives:
Option 1: Don't use OrigVar - always capture DD directly. The problem is that even when we always use
DD directly, EmitDeclRefLValue can still return an address with the wrong element type in some contexts (templates, different instantiation paths, etc.). So, this doesn't fully eliminate the need for the type adjustment, but simplifies the code.
Option 2: Fix target region codegen to preserve struct types for all captures For that we would need to modify `EmitDeclRefLValue` or target region capture emission to maintain precise LLVM struct types instead of using opaque pointers. This requires refactoring how OpenMP target regions handle captured parameters
globally, affecting all captures not just decompositions.
Option 3: Emit a bitcast instead of `withElementType`
`Address Addr = ...;`
`if (Addr.getElementType() != ExpectedTy) {`
`llvm::Value *Casted = Builder.CreateBitCast(Addr.getPointer(),`
`ExpectedTy->getPointerTo());`
`Addr = Address(Casted, ExpectedTy, Addr.getAlignment());`
` }`
This emits unnecessary bitcast instructions in IR when the pointer is already correct. This might generate worse IR.
Option 4: The current approach WithElementType.
Please advise with the best course of action.
https://github.com/llvm/llvm-project/pull/190832
More information about the cfe-commits
mailing list