diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -583,6 +583,14 @@ CGBuilderTy &Builder, const bool UsePointerValue = false); + /// Emit call to llvm.dbg.declare for a binding declaration. + /// Returns a pointer to the DILocalVariable associated with the + /// llvm.dbg.declare, or nullptr otherwise. + llvm::DILocalVariable *EmitDeclare(const BindingDecl *decl, llvm::Value *AI, + llvm::Optional ArgNo, + CGBuilderTy &Builder, + const bool UsePointerValue = false); + struct BlockByRefType { /// The wrapper struct used inside the __block_literal struct. llvm::DIType *BlockByRefWrapper; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -4635,11 +4635,103 @@ return D; } +llvm::DILocalVariable *CGDebugInfo::EmitDeclare(const BindingDecl *BD, + llvm::Value *Storage, + llvm::Optional ArgNo, + CGBuilderTy &Builder, + const bool UsePointerValue) { + assert(CGM.getCodeGenOpts().hasReducedDebugInfo()); + assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!"); + if (BD->hasAttr()) + return nullptr; + + // Skip the tuple like case, we don't handle that here + if (isa(BD->getBinding())) + return nullptr; + + llvm::DIFile *Unit = getOrCreateFile(BD->getLocation()); + llvm::DIType *Ty = getOrCreateType(BD->getType(), Unit); + + // If there is no debug info for this type then do not emit debug info + // for this variable. + if (!Ty) + return nullptr; + + auto Align = getDeclAlignIfRequired(BD, CGM.getContext()); + unsigned AddressSpace = CGM.getContext().getTargetAddressSpace(BD->getType()); + + SmallVector Expr; + AppendAddressSpaceXDeref(AddressSpace, Expr); + + // Clang stores the sret pointer provided by the caller in a static alloca. + // Use DW_OP_deref to tell the debugger to load the pointer and treat it as + // the address of the variable. + if (UsePointerValue) { + assert(!llvm::is_contained(Expr, llvm::dwarf::DW_OP_deref) && + "Debug info already contains DW_OP_deref."); + Expr.push_back(llvm::dwarf::DW_OP_deref); + } + + unsigned Line = getLineNumber(BD->getLocation()); + unsigned Column = getColumnNumber(BD->getLocation()); + StringRef Name = BD->getName(); + auto *Scope = cast(LexicalBlockStack.back()); + // Create the descriptor for the variable. + llvm::DILocalVariable *D = DBuilder.createAutoVariable( + Scope, Name, Unit, Line, Ty, CGM.getLangOpts().Optimize, + llvm::DINode::FlagZero, Align); + + if (const MemberExpr *ME = dyn_cast(BD->getBinding())) { + if (const FieldDecl *FD = dyn_cast(ME->getMemberDecl())) { + const unsigned fieldIndex = FD->getFieldIndex(); + const clang::CXXRecordDecl *parent = + (const CXXRecordDecl *)FD->getParent(); + const ASTRecordLayout &layout = + CGM.getContext().getASTRecordLayout(parent); + const uint64_t fieldOffset = layout.getFieldOffset(fieldIndex); + + if (fieldOffset != 0) { + Expr.push_back(llvm::dwarf::DW_OP_plus_uconst); + Expr.push_back( + CGM.getContext().toCharUnitsFromBits(fieldOffset).getQuantity()); + } + } + } else if (const ArraySubscriptExpr *ASE = + dyn_cast(BD->getBinding())) { + if (const IntegerLiteral *IL = dyn_cast(ASE->getIdx())) { + const uint64_t value = IL->getValue().getZExtValue(); + const uint64_t typeSize = CGM.getContext().getTypeSize(BD->getType()); + + if (value != 0) { + Expr.push_back(llvm::dwarf::DW_OP_plus_uconst); + Expr.push_back(CGM.getContext() + .toCharUnitsFromBits(value * typeSize) + .getQuantity()); + } + } + } + + // Insert an llvm.dbg.declare into the current block. + DBuilder.insertDeclare(Storage, D, DBuilder.createExpression(Expr), + llvm::DILocation::get(CGM.getLLVMContext(), Line, + Column, Scope, CurInlinedAt), + Builder.GetInsertBlock()); + + return D; +} + llvm::DILocalVariable * CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage, CGBuilderTy &Builder, const bool UsePointerValue) { assert(CGM.getCodeGenOpts().hasReducedDebugInfo()); + + if (auto *DD = dyn_cast(VD)) + for (auto *B : DD->bindings()) { + EmitDeclare(B, Storage, llvm::None, Builder, + VD->getType()->isReferenceType()); + } + return EmitDeclare(VD, Storage, llvm::None, Builder, UsePointerValue); } diff --git a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple %itanium_abi_triple %s -o - | FileCheck %s + +// CHECK: call void @llvm.dbg.declare(metadata %struct.A* %{{[0-9]+}}, metadata !{{[0-9]+}}, metadata !DIExpression()) +// CHECK: call void @llvm.dbg.declare(metadata %struct.A* %{{[0-9]+}}, metadata !{{[0-9]+}}, metadata !DIExpression(DW_OP_plus_uconst, {{[0-9]+}})) +// CHECK: call void @llvm.dbg.declare(metadata %struct.A* %{{[0-9]+}}, metadata !{{[0-9]+}}, metadata !DIExpression()) +// CHECK: call void @llvm.dbg.declare(metadata %struct.A** %{{[0-9]+}}, metadata !{{[0-9]+}}, metadata !DIExpression(DW_OP_deref)) +// CHECK: call void @llvm.dbg.declare(metadata %struct.A** %{{[0-9]+}}, metadata !{{[0-9]+}}, metadata !DIExpression(DW_OP_deref, DW_OP_plus_uconst, {{[0-9]+}})) +// CHECK: call void @llvm.dbg.declare(metadata %struct.A** %{{[0-9]+}}, metadata !{{[0-9]+}}, metadata !DIExpression()) +struct A { + int x; + int y; +}; + +int f() { + A a{10, 20}; + auto [x1, y1] = a; + auto &[x2, y2] = a; + return x1 + y1 + x2 + y2; +} diff --git a/lldb/test/API/lang/cpp/structured-binding/Makefile b/lldb/test/API/lang/cpp/structured-binding/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/structured-binding/Makefile @@ -0,0 +1,4 @@ +CXX_SOURCES := main.cpp +CXXFLAGS_EXTRAS := -std=c++17 + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py b/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py @@ -0,0 +1,84 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestStructuredBinding(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIf(compiler="clang", compiler_version=['<', '14.0']) + def test(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.cpp")) + + self.expect_expr("a1", result_type="A", + result_children=[ValueCheck(name="x", type="int"), + ValueCheck(name="y", type="int")]) + self.expect_expr("b1", result_type="char", result_value="'a'") + self.expect_expr("c1", result_type="char", result_value="'b'") + self.expect_expr("d1", result_type="short", result_value="50") + self.expect_expr("e1", result_type="int", result_value="60") + self.expect_expr("f1", result_type="char", result_value="'c'") + + self.expect_expr("a2", result_type="A", + result_children=[ValueCheck(name="x", type="int"), + ValueCheck(name="y", type="int")]) + self.expect_expr("b2", result_type="char", result_value="'a'") + self.expect_expr("c2", result_type="char", result_value="'b'") + self.expect_expr("d2", result_type="short", result_value="50") + self.expect_expr("e2", result_type="int", result_value="60") + self.expect_expr("f2", result_type="char", result_value="'c'") + + self.expect_expr("a3", result_type="A", + result_children=[ValueCheck(name="x", type="int"), + ValueCheck(name="y", type="int")]) + self.expect_expr("b3", result_type="char", result_value="'a'") + self.expect_expr("c3", result_type="char", result_value="'b'") + self.expect_expr("d3", result_type="short", result_value="50") + self.expect_expr("e3", result_type="int", result_value="60") + self.expect_expr("f3", result_type="char", result_value="'c'") + + self.expect_expr("carr_ref1", result_type="char", result_value="'a'") + self.expect_expr("carr_ref2", result_type="char", result_value="'b'") + self.expect_expr("carr_ref3", result_type="char", result_value="'c'") + + self.expect_expr("sarr_ref1", result_type="short", result_value="11") + self.expect_expr("sarr_ref2", result_type="short", result_value="12") + self.expect_expr("sarr_ref3", result_type="short", result_value="13") + + self.expect_expr("iarr_ref1", result_type="int", result_value="22") + self.expect_expr("iarr_ref2", result_type="int", result_value="33") + self.expect_expr("iarr_ref3", result_type="int", result_value="44") + + self.expect_expr("carr_rref1", result_type="char", result_value="'a'") + self.expect_expr("carr_rref2", result_type="char", result_value="'b'") + self.expect_expr("carr_rref3", result_type="char", result_value="'c'") + + self.expect_expr("sarr_rref1", result_type="short", result_value="11") + self.expect_expr("sarr_rref2", result_type="short", result_value="12") + self.expect_expr("sarr_rref3", result_type="short", result_value="13") + + self.expect_expr("iarr_rref1", result_type="int", result_value="22") + self.expect_expr("iarr_rref2", result_type="int", result_value="33") + self.expect_expr("iarr_rref3", result_type="int", result_value="44") + + self.expect_expr("carr_copy1", result_type="char", result_value="'a'") + self.expect_expr("carr_copy2", result_type="char", result_value="'b'") + self.expect_expr("carr_copy3", result_type="char", result_value="'c'") + + self.expect_expr("sarr_copy1", result_type="short", result_value="11") + self.expect_expr("sarr_copy2", result_type="short", result_value="12") + self.expect_expr("sarr_copy3", result_type="short", result_value="13") + + self.expect_expr("iarr_copy1", result_type="int", result_value="22") + self.expect_expr("iarr_copy2", result_type="int", result_value="33") + self.expect_expr("iarr_copy3", result_type="int", result_value="44") + + self.expect_expr("tx1", result_type="float", result_value="4") + self.expect_expr("ty1", result_type="char", result_value="'z'") + self.expect_expr("tz1", result_type="int", result_value="10") + + self.expect_expr("tx2", result_type="float", result_value="4") + self.expect_expr("ty2", result_type="char", result_value="'z'") + self.expect_expr("tz2", result_type="int", result_value="10") diff --git a/lldb/test/API/lang/cpp/structured-binding/main.cpp b/lldb/test/API/lang/cpp/structured-binding/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/structured-binding/main.cpp @@ -0,0 +1,69 @@ +// Structured binding in C++ can bind identifiers to subobjects of an object. +// +// There are three cases we need to test: +// 1) arrays +// 2) tuples like objects +// 3) non-static data members +// +// They can also bind by copy, reference or rvalue reference. + +#include + +struct A { + int x; + int y; +}; + +// We want to cover a mix of types and also different sizes to make sure we +// hande the offsets correctly. +struct MixedTypesAndSizesStruct { + A a; + char b1; + char b2; + short b3; + int b4; + char b5; +}; + +int main() { + MixedTypesAndSizesStruct b{{20, 30}, 'a', 'b', 50, 60, 'c'}; + + auto [a1, b1, c1, d1, e1, f1] = b; + auto &[a2, b2, c2, d2, e2, f2] = b; + auto &&[a3, b3, c3, d3, e3, f3] = + MixedTypesAndSizesStruct{{20, 30}, 'a', 'b', 50, 60, 'c'}; + + // Array with different sized types + char carr[]{'a', 'b', 'c'}; + short sarr[]{11, 12, 13}; + int iarr[]{22, 33, 44}; + + auto [carr_copy1, carr_copy2, carr_copy3] = carr; + auto [sarr_copy1, sarr_copy2, sarr_copy3] = sarr; + auto [iarr_copy1, iarr_copy2, iarr_copy3] = iarr; + + auto &[carr_ref1, carr_ref2, carr_ref3] = carr; + auto &[sarr_ref1, sarr_ref2, sarr_ref3] = sarr; + auto &[iarr_ref1, iarr_ref2, iarr_ref3] = iarr; + + auto &&[carr_rref1, carr_rref2, carr_rref3] = carr; + auto &&[sarr_rref1, sarr_rref2, sarr_rref3] = sarr; + auto &&[iarr_rref1, iarr_rref2, iarr_rref3] = iarr; + + float x{4.0}; + char y{'z'}; + int z{10}; + + std::tuple tpl(x, y, z); + auto [tx1, ty1, tz1] = tpl; + auto &[tx2, ty2, tz2] = tpl; + + return a1.x + b1 + c1 + d1 + e1 + f1 + a2.y + b2 + c2 + d2 + e2 + f2 + a3.x + + b3 + c3 + d3 + e3 + f3 + carr_copy1 + carr_copy2 + carr_copy3 + + sarr_copy1 + sarr_copy2 + sarr_copy3 + iarr_copy1 + iarr_copy2 + + iarr_copy3 + carr_ref1 + carr_ref2 + carr_ref3 + sarr_ref1 + + sarr_ref2 + sarr_ref3 + iarr_ref1 + iarr_ref2 + iarr_ref3 + + carr_rref1 + carr_rref2 + carr_rref3 + sarr_rref1 + sarr_rref2 + + sarr_rref3 + iarr_rref1 + iarr_rref2 + iarr_rref3 + tx1 + ty1 + tz1 + + tx2 + ty2 + tz2; // break here +}