Skip to content

Commit

Permalink
Debug Info: Support DW_AT_calling_convention on composite types.
Browse files Browse the repository at this point in the history
This implements the DWARF 5 feature described at
http://www.dwarfstd.org/ShowIssue.php?issue=141215.1

This allows a consumer to understand whether a composite data type is
trivially copyable and thus should be passed by value instead of by
reference. The canonical example is being able to distinguish the
following two types:

  // S is not trivially copyable because of the explicit destructor.
  struct S {
     ~S() {}
  };

  // T is a POD type.
  struct T {
     ~T() = default;
  };

This patch adds two new (DI)flags to LLVM metadata: TypePassByValue
and TypePassByReference.

<rdar://problem/36034922>
Differential Revision: https://reviews.llvm.org/D41743

llvm-svn: 321844
  • Loading branch information
adrian-prantl committed Jan 5, 2018
1 parent 0429ebf commit a29aac7
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 3 deletions.
2 changes: 2 additions & 0 deletions llvm/include/llvm-c/DebugInfo.h
Expand Up @@ -52,6 +52,8 @@ typedef enum {
LLVMDIFlagBitField = 1 << 19,
LLVMDIFlagNoReturn = 1 << 20,
LLVMDIFlagMainSubprogram = 1 << 21,
LLVMDIFlagTypePassByValue = 1 << 22,
LLVMDIFlagTypePassByReference = 1 << 23,
LLVMDIFlagIndirectVirtualBase = (1 << 2) | (1 << 5),
LLVMDIFlagAccessibility = LLVMDIFlagPrivate | LLVMDIFlagProtected |
LLVMDIFlagPublic,
Expand Down
4 changes: 3 additions & 1 deletion llvm/include/llvm/IR/DebugInfoFlags.def
Expand Up @@ -43,6 +43,8 @@ HANDLE_DI_FLAG((1 << 18), IntroducedVirtual)
HANDLE_DI_FLAG((1 << 19), BitField)
HANDLE_DI_FLAG((1 << 20), NoReturn)
HANDLE_DI_FLAG((1 << 21), MainSubprogram)
HANDLE_DI_FLAG((1 << 22), TypePassByValue)
HANDLE_DI_FLAG((1 << 23), TypePassByReference)

// To avoid needing a dedicated value for IndirectVirtualBase, we use
// the bitwise or of Virtual and FwdDecl, which does not otherwise
Expand All @@ -52,7 +54,7 @@ HANDLE_DI_FLAG((1 << 2) | (1 << 5), IndirectVirtualBase)
#ifdef DI_FLAG_LARGEST_NEEDED
// intended to be used with ADT/BitmaskEnum.h
// NOTE: always must be equal to largest flag, check this when adding new flag
HANDLE_DI_FLAG((1 << 21), Largest)
HANDLE_DI_FLAG((1 << 23), Largest)
#undef DI_FLAG_LARGEST_NEEDED
#endif

Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/IR/DebugInfoMetadata.h
Expand Up @@ -633,6 +633,10 @@ class DIType : public DIScope {
bool isStaticMember() const { return getFlags() & FlagStaticMember; }
bool isLValueReference() const { return getFlags() & FlagLValueReference; }
bool isRValueReference() const { return getFlags() & FlagRValueReference; }
bool isTypePassByValue() const { return getFlags() & FlagTypePassByValue; }
bool isTypePassByReference() const {
return getFlags() & FlagTypePassByReference;
}

static bool classof(const Metadata *MD) {
switch (MD->getMetadataID()) {
Expand Down
9 changes: 9 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
Expand Up @@ -975,6 +975,15 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
Tag == dwarf::DW_TAG_structure_type || Tag == dwarf::DW_TAG_union_type)
addTemplateParams(Buffer, CTy->getTemplateParams());

// Add the type's non-standard calling convention.
uint8_t CC = 0;
if (CTy->isTypePassByValue())
CC = dwarf::DW_CC_pass_by_value;
else if (CTy->isTypePassByReference())
CC = dwarf::DW_CC_pass_by_reference;
if (CC)
addUInt(Buffer, dwarf::DW_AT_calling_convention, dwarf::DW_FORM_data1,
CC);
break;
}
default:
Expand Down
7 changes: 5 additions & 2 deletions llvm/lib/IR/Verifier.cpp
Expand Up @@ -905,9 +905,12 @@ void Verifier::visitDIDerivedType(const DIDerivedType &N) {
}
}

/// Detect mutually exclusive flags.
static bool hasConflictingReferenceFlags(unsigned Flags) {
return (Flags & DINode::FlagLValueReference) &&
(Flags & DINode::FlagRValueReference);
return ((Flags & DINode::FlagLValueReference) &&
(Flags & DINode::FlagRValueReference)) ||
((Flags & DINode::FlagTypePassByValue) &&
(Flags & DINode::FlagTypePassByReference));
}

void Verifier::visitTemplateParams(const MDNode &N, const Metadata &RawParams) {
Expand Down
63 changes: 63 additions & 0 deletions llvm/test/DebugInfo/Generic/pass-by-value.ll
@@ -0,0 +1,63 @@
; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck %s
;
; // S is not trivially copyable.
; struct S {
; ~S() {}
; };
;
; // T is a POD.
; struct T {
; ~T() = default;
; };
;
; S s;
; T t;
;
; CHECK: DW_TAG_structure_type
; CHECK-NEXT: DW_AT_calling_convention (DW_CC_pass_by_reference)
; CHECK-NEXT: DW_AT_name ("S")
;
; CHECK: DW_TAG_structure_type
; CHECK-NEXT: DW_AT_calling_convention (DW_CC_pass_by_value)
; CHECK-NEXT: DW_AT_name ("T")

source_filename = "pass.cpp"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.13.0"

%struct.S = type { i8 }
%struct.T = type { i8 }

@s = global %struct.S zeroinitializer, align 1, !dbg !0
@__dso_handle = external hidden global i8
@t = global %struct.T zeroinitializer, align 1, !dbg !6

!llvm.dbg.cu = !{!2}
!llvm.module.flags = !{!20, !21, !22, !23}
!llvm.ident = !{!24}

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "s", scope: !2, file: !3, line: 9, type: !14, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "clang version 7.0.0 (trunk 321763) (llvm/trunk 321758)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5)
!3 = !DIFile(filename: "pass.cpp", directory: "/")
!4 = !{}
!5 = !{!0, !6}
!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression())
!7 = distinct !DIGlobalVariable(name: "t", scope: !2, file: !3, line: 10, type: !8, isLocal: false, isDefinition: true)
!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "T", file: !3, line: 5, size: 8, elements: !9, identifier: "_ZTS1T", flags: DIFlagTypePassByValue)
!9 = !{!10}
!10 = !DISubprogram(name: "~T", scope: !8, file: !3, line: 6, type: !11, isLocal: false, isDefinition: false, scopeLine: 6, flags: DIFlagPrototyped, isOptimized: false)
!11 = !DISubroutineType(types: !12)
!12 = !{null, !13}
!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !3, line: 1, size: 8, elements: !15, identifier: "_ZTS1S", flags: DIFlagTypePassByReference)
!15 = !{!16}
!16 = !DISubprogram(name: "~S", scope: !14, file: !3, line: 2, type: !17, isLocal: false, isDefinition: false, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false)
!17 = !DISubroutineType(types: !18)
!18 = !{null, !19}
!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
!20 = !{i32 2, !"Dwarf Version", i32 4}
!21 = !{i32 2, !"Debug Info Version", i32 3}
!22 = !{i32 1, !"wchar_size", i32 4}
!23 = !{i32 7, !"PIC Level", i32 2}
!24 = !{!"clang version 7.0.0 (trunk 321763) (llvm/trunk 321758)"}
5 changes: 5 additions & 0 deletions llvm/test/Verifier/cc-flags.ll
@@ -0,0 +1,5 @@
; RUN: not opt -S < %s 2>&1 | FileCheck %s

!named = !{!0}
!0 = !DICompositeType(tag: DW_TAG_structure_type, name: "A", size: 1, flags: DIFlagTypePassByReference | DIFlagTypePassByValue)
; CHECK: invalid reference flags

0 comments on commit a29aac7

Please sign in to comment.