================
@@ -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
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to