Skip to content

Commit

Permalink
[CodeView] Add support for ref-qualified member functions.
Browse files Browse the repository at this point in the history
When you have a member function with a ref-qualifier, for example:

struct Foo {
  void Func() &;
  void Func2() &&;
};

clang-cl was not emitting this information. Doing so is a bit
awkward, because it's not a property of the LF_MFUNCTION type, which
is what you'd expect. Instead, it's a property of the this pointer
which is actually an LF_POINTER. This record has an attributes
bitmask on it, and our handling of this bitmask was all wrong. We
had some parts of the bitmask defined incorrectly, but importantly
for this bug, we didn't know about these extra 2 bits that represent
the ref qualifier at all.

Differential Revision: https://reviews.llvm.org/D54667

llvm-svn: 347354
  • Loading branch information
Zachary Turner committed Nov 20, 2018
1 parent 3826566 commit c68f895
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 24 deletions.
4 changes: 3 additions & 1 deletion llvm/include/llvm/DebugInfo/CodeView/CodeView.h
Expand Up @@ -358,7 +358,9 @@ enum class PointerOptions : uint32_t {
Const = 0x00000400,
Unaligned = 0x00000800,
Restrict = 0x00001000,
WinRTSmartPointer = 0x00080000
WinRTSmartPointer = 0x00080000,
LValueRefThisPointer = 0x00100000,
RValueRefThisPointer = 0x00200000
};
CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(PointerOptions)

Expand Down
16 changes: 14 additions & 2 deletions llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h
Expand Up @@ -264,14 +264,18 @@ class StringListRecord : public TypeRecord {
// LF_POINTER
class PointerRecord : public TypeRecord {
public:
// ---------------------------XXXXX
static const uint32_t PointerKindShift = 0;
static const uint32_t PointerKindMask = 0x1F;

// ------------------------XXX-----
static const uint32_t PointerModeShift = 5;
static const uint32_t PointerModeMask = 0x07;

static const uint32_t PointerOptionMask = 0xFF;
// ----------XXX------XXXXX-------
static const uint32_t PointerOptionMask = 0x1C0F80;

// -------------XXXXXX------------
static const uint32_t PointerSizeShift = 13;
static const uint32_t PointerSizeMask = 0xFF;

Expand Down Expand Up @@ -305,7 +309,7 @@ class PointerRecord : public TypeRecord {
}

PointerOptions getOptions() const {
return static_cast<PointerOptions>(Attrs);
return static_cast<PointerOptions>(Attrs & PointerOptionMask);
}

uint8_t getSize() const {
Expand Down Expand Up @@ -334,6 +338,14 @@ class PointerRecord : public TypeRecord {
return !!(Attrs & uint32_t(PointerOptions::Restrict));
}

bool isLValueReferenceThisPtr() const {
return !!(Attrs & uint32_t(PointerOptions::LValueRefThisPointer));
}

bool isRValueReferenceThisPtr() const {
return !!(Attrs & uint32_t(PointerOptions::RValueRefThisPointer));
}

TypeIndex ReferentType;
uint32_t Attrs;
Optional<MemberPointerInfo> MemberInfo;
Expand Down
66 changes: 45 additions & 21 deletions llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
Expand Up @@ -1882,27 +1882,22 @@ TypeIndex CodeViewDebug::lowerTypeMemberFunction(const DISubroutineType *Ty,
// Lower the containing class type.
TypeIndex ClassType = getTypeIndex(ClassTy);

SmallVector<TypeIndex, 8> ReturnAndArgTypeIndices;
for (DITypeRef ArgTypeRef : Ty->getTypeArray())
ReturnAndArgTypeIndices.push_back(getTypeIndex(ArgTypeRef));
DITypeRefArray ReturnAndArgs = Ty->getTypeArray();

unsigned Index = 0;
SmallVector<TypeIndex, 8> ArgTypeIndices;
TypeIndex ReturnTypeIndex = getTypeIndex(ReturnAndArgs[Index++]);

// MSVC uses type none for variadic argument.
if (ReturnAndArgTypeIndices.size() > 1 &&
ReturnAndArgTypeIndices.back() == TypeIndex::Void()) {
ReturnAndArgTypeIndices.back() = TypeIndex::None();
}
TypeIndex ReturnTypeIndex = TypeIndex::Void();
ArrayRef<TypeIndex> ArgTypeIndices = None;
if (!ReturnAndArgTypeIndices.empty()) {
auto ReturnAndArgTypesRef = makeArrayRef(ReturnAndArgTypeIndices);
ReturnTypeIndex = ReturnAndArgTypesRef.front();
ArgTypeIndices = ReturnAndArgTypesRef.drop_front();
}
TypeIndex ThisTypeIndex;
if (!IsStaticMethod && !ArgTypeIndices.empty()) {
ThisTypeIndex = ArgTypeIndices.front();
ArgTypeIndices = ArgTypeIndices.drop_front();
}
if (!IsStaticMethod && ReturnAndArgs.size() > 1)
ThisTypeIndex = getTypeIndexForThisPtr(ReturnAndArgs[Index++], Ty);

while (Index < ReturnAndArgs.size())
ArgTypeIndices.push_back(getTypeIndex(ReturnAndArgs[Index++]));

// MSVC uses type none for variadic argument.
if (!ArgTypeIndices.empty() && ArgTypeIndices.back() == TypeIndex::Void())
ArgTypeIndices.back() = TypeIndex::None();

ArgListRecord ArgListRec(TypeRecordKind::ArgList, ArgTypeIndices);
TypeIndex ArgListIndex = TypeTable.writeLeafType(ArgListRec);
Expand Down Expand Up @@ -1992,8 +1987,8 @@ static ClassOptions getCommonClassOptions(const DICompositeType *Ty) {
CO |= ClassOptions::Nested;

// Put the Scoped flag on function-local types. MSVC puts this flag for enum
// type only when it has an immediate function scope. Clang never puts enums
// inside DILexicalBlock scopes. Enum types, as generated by clang, are
// type only when it has an immediate function scope. Clang never puts enums
// inside DILexicalBlock scopes. Enum types, as generated by clang, are
// always in function, class, or file scopes.
if (Ty->getTag() == dwarf::DW_TAG_enumeration_type) {
if (ImmediateScope && isa<DISubprogram>(ImmediateScope))
Expand Down Expand Up @@ -2449,6 +2444,35 @@ TypeIndex CodeViewDebug::getTypeIndex(DITypeRef TypeRef, DITypeRef ClassTyRef) {
return recordTypeIndexForDINode(Ty, TI, ClassTy);
}

codeview::TypeIndex
CodeViewDebug::getTypeIndexForThisPtr(DITypeRef TypeRef,
const DISubroutineType *SubroutineTy) {
const DIType *Ty = TypeRef.resolve();

PointerOptions Options = PointerOptions::None;
if (SubroutineTy->getFlags() & DINode::DIFlags::FlagLValueReference)
Options = PointerOptions::LValueRefThisPointer;
else if (SubroutineTy->getFlags() & DINode::DIFlags::FlagRValueReference)
Options = PointerOptions::RValueRefThisPointer;

// Check if we've already translated this type. If there is no ref qualifier
// on the function then we look up this pointer type with no associated class
// so that the TypeIndex for the this pointer can be shared with the type
// index for other pointers to this class type. If there is a ref qualifier
// then we lookup the pointer using the subroutine as the parent type.
const DIType *ParentTy = nullptr;
if (Options != PointerOptions::None)
ParentTy = SubroutineTy;

auto I = TypeIndices.find({Ty, SubroutineTy});
if (I != TypeIndices.end())
return I->second;

TypeLoweringScope S(*this);
TypeIndex TI = lowerTypePointer(cast<DIDerivedType>(Ty), Options);
return recordTypeIndexForDINode(Ty, TI, SubroutineTy);
}

TypeIndex CodeViewDebug::getTypeIndexForReferenceTo(DITypeRef TypeRef) {
DIType *Ty = TypeRef.resolve();
PointerRecord PR(getTypeIndex(Ty),
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
Expand Up @@ -346,6 +346,10 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
codeview::TypeIndex getTypeIndex(DITypeRef TypeRef,
DITypeRef ClassTyRef = DITypeRef());

codeview::TypeIndex
getTypeIndexForThisPtr(DITypeRef TypeRef,
const DISubroutineType *SubroutineTy);

codeview::TypeIndex getTypeIndexForReferenceTo(DITypeRef TypeRef);

codeview::TypeIndex getMemberFunctionType(const DISubprogram *SP,
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp
Expand Up @@ -370,6 +370,8 @@ Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, PointerRecord &Ptr) {
W->printNumber("IsVolatile", Ptr.isVolatile());
W->printNumber("IsUnaligned", Ptr.isUnaligned());
W->printNumber("IsRestrict", Ptr.isRestrict());
W->printNumber("IsThisPtr&", Ptr.isLValueReferenceThisPtr());
W->printNumber("IsThisPtr&&", Ptr.isRValueReferenceThisPtr());
W->printNumber("SizeOf", Ptr.getSize());

if (Ptr.isPointerToMember()) {
Expand Down
205 changes: 205 additions & 0 deletions llvm/test/DebugInfo/COFF/types-method-ref-qualifiers.ll
@@ -0,0 +1,205 @@
; RUN: llc < %s -filetype=obj | llvm-readobj - -codeview | FileCheck %s

; C++ source to regenerate:
; struct A {
; int NoRefQual();
;
; int RefQual() &;
; int RefQual() &&;
;
; int LValueRef() &;
;
; int RValueRef() &&;
; };
;
; void foo() {
; A *GenericPtr = nullptr;
; A a;
; }


; ModuleID = 'foo.cpp'
source_filename = "foo.cpp"
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc19.15.26732"

%struct.A = type { i8 }

; Function Attrs: noinline nounwind optnone uwtable
define dso_local void @"?foo@@YAXXZ"() #0 !dbg !10 {
entry:
%GenericPtr = alloca %struct.A*, align 8
%a = alloca %struct.A, align 1
call void @llvm.dbg.declare(metadata %struct.A** %GenericPtr, metadata !13, metadata !DIExpression()), !dbg !28
store %struct.A* null, %struct.A** %GenericPtr, align 8, !dbg !28
call void @llvm.dbg.declare(metadata %struct.A* %a, metadata !29, metadata !DIExpression()), !dbg !30
ret void, !dbg !31
}

; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.declare(metadata, metadata, metadata) #1

attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone speculatable }

!llvm.dbg.cu = !{!0}
!llvm.linker.options = !{!3, !4}
!llvm.module.flags = !{!5, !6, !7, !8}
!llvm.ident = !{!9}

!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 8.0.0 ", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "foo.cpp", directory: "D:\5C\5Csrc\5C\5Cllvmbuild\5C\5Cninja-x64", checksumkind: CSK_MD5, checksum: "d1b6ae9dc9ab85ca0a41c8b8c79a0b6a")
!2 = !{}
!3 = !{!"/DEFAULTLIB:libcmt.lib"}
!4 = !{!"/DEFAULTLIB:oldnames.lib"}
!5 = !{i32 2, !"CodeView", i32 1}
!6 = !{i32 2, !"Debug Info Version", i32 3}
!7 = !{i32 1, !"wchar_size", i32 2}
!8 = !{i32 7, !"PIC Level", i32 2}
!9 = !{!"clang version 8.0.0 "}
!10 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAXXZ", scope: !1, file: !1, line: 12, type: !11, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2)
!11 = !DISubroutineType(types: !12)
!12 = !{null}
!13 = !DILocalVariable(name: "GenericPtr", scope: !10, file: !1, line: 13, type: !14)
!14 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !15, size: 64)
!15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !1, line: 1, size: 8, flags: DIFlagTypePassByValue | DIFlagTrivial, elements: !16, identifier: ".?AUA@@")
!16 = !{!17, !22, !24, !26, !27}
!17 = !DISubprogram(name: "NoRefQual", linkageName: "?NoRefQual@A@@QEAAHXZ", scope: !15, file: !1, line: 2, type: !18, isLocal: false, isDefinition: false, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false)
!18 = !DISubroutineType(types: !19)
!19 = !{!20, !21}
!20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !15, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
!22 = !DISubprogram(name: "RefQual", linkageName: "?RefQual@A@@QEGAAHXZ", scope: !15, file: !1, line: 4, type: !23, isLocal: false, isDefinition: false, scopeLine: 4, flags: DIFlagPrototyped | DIFlagLValueReference, isOptimized: false)
!23 = !DISubroutineType(flags: DIFlagLValueReference, types: !19)
!24 = !DISubprogram(name: "RefQual", linkageName: "?RefQual@A@@QEHAAHXZ", scope: !15, file: !1, line: 5, type: !25, isLocal: false, isDefinition: false, scopeLine: 5, flags: DIFlagPrototyped | DIFlagRValueReference, isOptimized: false)
!25 = !DISubroutineType(flags: DIFlagRValueReference, types: !19)
!26 = !DISubprogram(name: "LValueRef", linkageName: "?LValueRef@A@@QEGAAHXZ", scope: !15, file: !1, line: 7, type: !23, isLocal: false, isDefinition: false, scopeLine: 7, flags: DIFlagPrototyped | DIFlagLValueReference, isOptimized: false)
!27 = !DISubprogram(name: "RValueRef", linkageName: "?RValueRef@A@@QEHAAHXZ", scope: !15, file: !1, line: 9, type: !25, isLocal: false, isDefinition: false, scopeLine: 9, flags: DIFlagPrototyped | DIFlagRValueReference, isOptimized: false)
!28 = !DILocation(line: 13, scope: !10)
!29 = !DILocalVariable(name: "a", scope: !10, file: !1, line: 14, type: !15)
!30 = !DILocation(line: 14, scope: !10)
!31 = !DILocation(line: 15, scope: !10)




; CHECK: CodeViewTypes [
; CHECK: Section: .debug$T (7)
; CHECK: Magic: 0x4
; CHECK: Pointer (0x1005) {
; CHECK: TypeLeafKind: LF_POINTER (0x1002)
; CHECK: PointeeType: A (0x1003)
; CHECK: PtrType: Near64 (0xC)
; CHECK: PtrMode: Pointer (0x0)
; CHECK: IsFlat: 0
; CHECK: IsConst: 1
; CHECK: IsVolatile: 0
; CHECK: IsUnaligned: 0
; CHECK: IsRestrict: 0
; CHECK: IsThisPtr&: 0
; CHECK: IsThisPtr&&: 0
; CHECK: SizeOf: 8
; CHECK: }
; CHECK: MemberFunction (0x1006) {
; CHECK: TypeLeafKind: LF_MFUNCTION (0x1009)
; CHECK: ReturnType: int (0x74)
; CHECK: ClassType: A (0x1003)
; CHECK: ThisType: A* const (0x1005)
; CHECK: CallingConvention: NearC (0x0)
; CHECK: FunctionOptions [ (0x0)
; CHECK: ]
; CHECK: NumParameters: 0
; CHECK: ArgListType: () (0x1000)
; CHECK: ThisAdjustment: 0
; CHECK: }
; CHECK: Pointer (0x1007) {
; CHECK: TypeLeafKind: LF_POINTER (0x1002)
; CHECK: PointeeType: A (0x1003)
; CHECK: PtrType: Near64 (0xC)
; CHECK: PtrMode: Pointer (0x0)
; CHECK: IsFlat: 0
; CHECK: IsConst: 1
; CHECK: IsVolatile: 0
; CHECK: IsUnaligned: 0
; CHECK: IsRestrict: 0
; CHECK: IsThisPtr&: 1
; CHECK: IsThisPtr&&: 0
; CHECK: SizeOf: 136
; CHECK: }
; CHECK: MemberFunction (0x1008) {
; CHECK: TypeLeafKind: LF_MFUNCTION (0x1009)
; CHECK: ReturnType: int (0x74)
; CHECK: ClassType: A (0x1003)
; CHECK: ThisType: A* const (0x1007)
; CHECK: CallingConvention: NearC (0x0)
; CHECK: FunctionOptions [ (0x0)
; CHECK: ]
; CHECK: NumParameters: 0
; CHECK: ArgListType: () (0x1000)
; CHECK: ThisAdjustment: 0
; CHECK: }
; CHECK: Pointer (0x1009) {
; CHECK: TypeLeafKind: LF_POINTER (0x1002)
; CHECK: PointeeType: A (0x1003)
; CHECK: PtrType: Near64 (0xC)
; CHECK: PtrMode: Pointer (0x0)
; CHECK: IsFlat: 0
; CHECK: IsConst: 1
; CHECK: IsVolatile: 0
; CHECK: IsUnaligned: 0
; CHECK: IsRestrict: 0
; CHECK: IsThisPtr&: 0
; CHECK: IsThisPtr&&: 1
; CHECK: SizeOf: 8
; CHECK: }
; CHECK: MemberFunction (0x100A) {
; CHECK: TypeLeafKind: LF_MFUNCTION (0x1009)
; CHECK: ReturnType: int (0x74)
; CHECK: ClassType: A (0x1003)
; CHECK: ThisType: A* const (0x1009)
; CHECK: CallingConvention: NearC (0x0)
; CHECK: FunctionOptions [ (0x0)
; CHECK: ]
; CHECK: NumParameters: 0
; CHECK: ArgListType: () (0x1000)
; CHECK: ThisAdjustment: 0
; CHECK: }
; CHECK: MethodOverloadList (0x100B) {
; CHECK: TypeLeafKind: LF_METHODLIST (0x1206)
; CHECK: Method [
; CHECK: AccessSpecifier: Public (0x3)
; CHECK: Type: int A::() (0x1008)
; CHECK: ]
; CHECK: Method [
; CHECK: AccessSpecifier: Public (0x3)
; CHECK: Type: int A::() (0x100A)
; CHECK: ]
; CHECK: }
; CHECK: FieldList (0x100C) {
; CHECK: TypeLeafKind: LF_FIELDLIST (0x1203)
; CHECK: OneMethod {
; CHECK: TypeLeafKind: LF_ONEMETHOD (0x1511)
; CHECK: AccessSpecifier: Public (0x3)
; CHECK: Type: int A::() (0x1006)
; CHECK: Name: NoRefQual
; CHECK: }
; CHECK: OverloadedMethod {
; CHECK: TypeLeafKind: LF_METHOD (0x150F)
; CHECK: MethodCount: 0x2
; CHECK: MethodListIndex: 0x100B
; CHECK: Name: RefQual
; CHECK: }
; CHECK: OneMethod {
; CHECK: TypeLeafKind: LF_ONEMETHOD (0x1511)
; CHECK: AccessSpecifier: Public (0x3)
; CHECK: Type: int A::() (0x1008)
; CHECK: Name: LValueRef
; CHECK: }
; CHECK: OneMethod {
; CHECK: TypeLeafKind: LF_ONEMETHOD (0x1511)
; CHECK: AccessSpecifier: Public (0x3)
; CHECK: Type: int A::() (0x100A)
; CHECK: Name: RValueRef
; CHECK: }
; CHECK: }
; CHECK: ]

0 comments on commit c68f895

Please sign in to comment.