[clang][OpenMP][DebugInfo] Debug support for variables in shared clause of OpenMP task construct

Currently variables appearing inside shared clause of OpenMP task construct
are not visible inside lldb debugger.

After the current patch, lldb is able to show the variable

```
* thread #1, name = 'a.out', stop reason = breakpoint 1.1
    frame #0: 0x0000000000400934 a.out`.omp_task_entry. [inlined] .omp_outlined.(.global_tid.=0, .part_id.=0x000000000071f0d0, .privates.=0x000000000071f0e8, .copy_fn.=(a.out`.omp_task_privates_map. at testshared.cxx:8), .task_t.=0x000000000071f0c0, __context=0x000000000071f0f0) at testshared.cxx:10:34
   7      else {
   8    #pragma omp task shared(svar) firstprivate(n)
   9        {
-> 10         printf("Task svar = %d\n", svar);
   11         printf("Task n = %d\n", n);
   12         svar = fib(n - 1);
   13       }
(lldb) p svar
(int) $0 = 9
```

Reviewed By: djtodoro

Differential Revision: https://reviews.llvm.org/D115510
This commit is contained in:
Alok Kumar Sharma 2021-12-22 19:56:37 +05:30
parent a9bb97e841
commit 5eb271880c
3 changed files with 110 additions and 0 deletions

View File

@ -24,10 +24,13 @@
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/OpenMPKinds.h"
#include "clang/Basic/PrettyStackTrace.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Metadata.h"
#include "llvm/Support/AtomicOrdering.h"
using namespace clang;
using namespace CodeGen;
@ -4431,6 +4434,53 @@ void CodeGenFunction::EmitOMPTaskBasedDirective(
UntiedLocalVars;
// Set proper addresses for generated private copies.
OMPPrivateScope Scope(CGF);
// Generate debug info for variables present in shared clause.
if (auto *DI = CGF.getDebugInfo()) {
llvm::SmallDenseMap<const VarDecl *, FieldDecl *> CaptureFields =
CGF.CapturedStmtInfo->getCaptureFields();
llvm::Value *ContextValue = CGF.CapturedStmtInfo->getContextValue();
if (CaptureFields.size() && ContextValue) {
unsigned CharWidth = CGF.getContext().getCharWidth();
// The shared variables are packed together as members of structure.
// So the address of each shared variable can be computed by adding
// offset of it (within record) to the base address of record. For each
// shared variable, debug intrinsic llvm.dbg.declare is generated with
// appropriate expressions (DIExpression).
// Ex:
// %12 = load %struct.anon*, %struct.anon** %__context.addr.i
// call void @llvm.dbg.declare(metadata %struct.anon* %12,
// metadata !svar1,
// metadata !DIExpression(DW_OP_deref))
// call void @llvm.dbg.declare(metadata %struct.anon* %12,
// metadata !svar2,
// metadata !DIExpression(DW_OP_plus_uconst, 8, DW_OP_deref))
for (auto It = CaptureFields.begin(); It != CaptureFields.end(); ++It) {
const VarDecl *SharedVar = It->first;
RecordDecl *CaptureRecord = It->second->getParent();
const ASTRecordLayout &Layout =
CGF.getContext().getASTRecordLayout(CaptureRecord);
unsigned Offset =
Layout.getFieldOffset(It->second->getFieldIndex()) / CharWidth;
(void)DI->EmitDeclareOfAutoVariable(SharedVar, ContextValue,
CGF.Builder, false);
llvm::Instruction &Last = CGF.Builder.GetInsertBlock()->back();
// Get the call dbg.declare instruction we just created and update
// its DIExpression to add offset to base address.
if (auto DDI = dyn_cast<llvm::DbgVariableIntrinsic>(&Last)) {
SmallVector<uint64_t, 8> Ops;
// Add offset to the base address if non zero.
if (Offset) {
Ops.push_back(llvm::dwarf::DW_OP_plus_uconst);
Ops.push_back(Offset);
}
Ops.push_back(llvm::dwarf::DW_OP_deref);
auto &Ctx = DDI->getContext();
llvm::DIExpression *DIExpr = llvm::DIExpression::get(Ctx, Ops);
Last.setOperand(2, llvm::MetadataAsValue::get(Ctx, DIExpr));
}
}
}
}
llvm::SmallVector<std::pair<const VarDecl *, Address>, 16> FirstprivatePtrs;
if (!Data.PrivateVars.empty() || !Data.FirstprivateVars.empty() ||
!Data.LastprivateVars.empty() || !Data.PrivateLocals.empty()) {

View File

@ -459,6 +459,11 @@ public:
/// Get the name of the capture helper.
virtual StringRef getHelperName() const { return "__captured_stmt"; }
/// Get the CaptureFields
llvm::SmallDenseMap<const VarDecl *, FieldDecl *> getCaptureFields() {
return CaptureFields;
}
private:
/// The kind of captured statement being generated.
CapturedRegionKind Kind;

View File

@ -0,0 +1,55 @@
// This testcase checks emission of debug info for variables
// inside shared clause of task construct.
// REQUIRES: x86_64-linux
// RUN: %clang_cc1 -debug-info-kind=constructor -DSHARED -x c -verify -triple x86_64-pc-linux-gnu -fopenmp -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK
// RUN: %clang_cc1 -debug-info-kind=constructor -x c -verify -triple x86_64-pc-linux-gnu -fopenmp -emit-llvm %s -o - | FileCheck %s --check-prefix=NEG
// expected-no-diagnostics
// CHECK-LABEL: define internal i32 @.omp_task_entry.
// CHECK-DAG: [[CONTEXT:%[0-9]+]] = load %struct.anon*, %struct.anon** %__context.addr.i, align 8
// CHECK-DAG: call void @llvm.dbg.declare(metadata %struct.anon* [[CONTEXT]], metadata [[SHARE2:![0-9]+]], metadata !DIExpression(DW_OP_deref))
// CHECK-DAG: call void @llvm.dbg.declare(metadata %struct.anon* [[CONTEXT]], metadata [[SHARE3:![0-9]+]], metadata !DIExpression(DW_OP_plus_uconst, 8, DW_OP_deref))
// CHECK-DAG: call void @llvm.dbg.declare(metadata %struct.anon* [[CONTEXT]], metadata [[SHARE1:![0-9]+]], metadata !DIExpression(DW_OP_plus_uconst, 16, DW_OP_deref))
// CHECK-DAG: [[SHARE2]] = !DILocalVariable(name: "share2"
// CHECK-DAG: [[SHARE3]] = !DILocalVariable(name: "share3"
// CHECK-DAG: [[SHARE1]] = !DILocalVariable(name: "share1"
// NEG-LABEL: define internal i32 @.omp_task_entry.
// NEG: [[CONTEXT:%[0-9]+]] = load %struct.anon*, %struct.anon** %__context.addr.i, align 8
// NEG-NOT: call void @llvm.dbg.declare(metadata %struct.anon* [[CONTEXT]], metadata {{![0-9]+}}, metadata !DIExpression(DW_OP_deref))
extern int printf(const char *, ...);
int foo(int n) {
int share1 = 9, share2 = 11, share3 = 13, priv1, priv2, fpriv;
fpriv = n + 4;
if (n < 2)
return n;
else {
#if SHARED
#pragma omp task shared(share1, share2) private(priv1, priv2) firstprivate(fpriv) shared(share3)
#else
#pragma omp task private(priv1, priv2) firstprivate(fpriv)
#endif
{
priv1 = n;
priv2 = n + 2;
share2 += share3;
printf("share1 = %d, share2 = %d, share3 = %d\n", share1, share2, share3);
share1 = priv1 + priv2 + fpriv + foo(n - 1) + share2 + share3;
}
#pragma omp taskwait
return share1 + share2 + share3;
}
}
int main() {
int n = 10;
printf("foo(%d) = %d\n", n, foo(n));
return 0;
}