Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -94,6 +94,7 @@ struct LocalVariable { const DILocalVariable *DIVar = nullptr; SmallVector DefRanges; + bool Deref; }; struct InlineSite { @@ -259,6 +260,8 @@ codeview::TypeIndex getTypeIndex(DITypeRef TypeRef, DITypeRef ClassTyRef = DITypeRef()); + codeview::TypeIndex getTypeIndexForReferenceTo(DITypeRef TypeRef); + codeview::TypeIndex getMemberFunctionType(const DISubprogram *SP, const DICompositeType *Class); Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -945,6 +945,7 @@ LocalVariable Var; Var.DIVar = VI.Var; Var.DefRanges.emplace_back(std::move(DefRange)); + Var.Deref = false; recordLocalVariable(std::move(Var), VI.Loc->getInlinedAt()); } } @@ -979,6 +980,16 @@ Var.DIVar = DIVar; // Calculate the definition ranges. + // Because we cannot express DW_OP_deref in CodeView directly, + // we use a trick: we encode the type as a reference to the + // real type. Unfortunately, this means we cannot mix deref + // defranges with non-deref defranges. We track how much of + // the code is covered by each, and use the variant which + // gives us the most coverage. + uint64_t DerefLength = 0; + uint64_t NonDerefLength = 0; + SmallVector DerefDefRanges; + SmallVector NonDerefDefRanges; for (auto I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { const InsnRange &Range = *I; const MachineInstr *DVInst = Range.first; @@ -987,27 +998,55 @@ bool InMemory = DVInst->getOperand(1).isImm(); bool IsSubfield = false; unsigned StructOffset = 0; - // Recognize a +Offset expression. + Optional Fragment; + // Read DIExpr to get the offset (if any). + // We only handle expressions generated by DIExpression::appendOffset, + // which doesn't require a full stack machine. int Offset = 0; - DIExpressionCursor Ops(DIExpr); - auto Op = Ops.peek(); - if (Op && Op->getOp() == dwarf::DW_OP_plus_uconst) { - Offset = Op->getArg(0); - Ops.take(); + bool Supported = true; + bool Deref = false; + auto Op = DIExpr->expr_op_begin(); + while (Supported && Op != DIExpr->expr_op_end()) { + switch (Op->getOp()) { + case dwarf::DW_OP_constu: { + int Value = Op->getArg(0); + ++Op; + if (Op != DIExpr->expr_op_end()) { + switch (Op->getOp()) { + case dwarf::DW_OP_minus: + Offset -= Value; + break; + case dwarf::DW_OP_plus: + Offset += Value; + default: + continue; + } + } + } break; + case dwarf::DW_OP_plus_uconst: + Offset += Op->getArg(0); + break; + case dwarf::DW_OP_LLVM_fragment: + Fragment = {Op->getArg(1), Op->getArg(0)}; + break; + case dwarf::DW_OP_deref: + Deref = true; + break; + default: + Supported = false; + } + ++Op; } + // If we don't know how to handle this range, skip past it. + if (!Supported || (Offset && !InMemory)) + continue; + // Handle fragments. - auto Fragment = Ops.getFragmentInfo(); if (Fragment) { IsSubfield = true; StructOffset = Fragment->OffsetInBits / 8; } - // Ignore unrecognized exprs. - if (Ops.peek() && Ops.peek()->getOp() != dwarf::DW_OP_LLVM_fragment) - continue; - if (!InMemory && Offset) - continue; - // Bail if operand 0 is not a valid register. This means the variable is a // simple constant, or is described by a complex expression. // FIXME: Find a way to represent constant variables, since they are @@ -1017,6 +1056,10 @@ if (Reg == 0) continue; + SmallVector &DefRanges = + Deref ? DerefDefRanges : NonDerefDefRanges; + uint64_t &Length = Deref ? DerefLength : NonDerefLength; + // Handle the two cases we can handle: indirect in memory and in register. unsigned CVReg = TRI->getCodeViewRegNum(Reg); { @@ -1027,9 +1070,8 @@ DR.IsSubfield = IsSubfield; DR.StructOffset = StructOffset; - if (Var.DefRanges.empty() || - Var.DefRanges.back().isDifferentLocation(DR)) { - Var.DefRanges.emplace_back(std::move(DR)); + if (DefRanges.empty() || DefRanges.back().isDifferentLocation(DR)) { + DefRanges.emplace_back(std::move(DR)); } } @@ -1048,11 +1090,13 @@ else End = Asm->getFunctionEnd(); } + assert(End); + Length += End->getOffset() - Begin->getOffset(); // If the last range end is our begin, just extend the last range. // Otherwise make a new range. SmallVectorImpl> &Ranges = - Var.DefRanges.back().Ranges; + DefRanges.back().Ranges; if (!Ranges.empty() && Ranges.back().second == Begin) Ranges.back().second = End; else @@ -1061,6 +1105,14 @@ // FIXME: Do more range combining. } + if (DerefLength > NonDerefLength) { + Var.Deref = true; + Var.DefRanges = std::move(DerefDefRanges); + } else { + Var.Deref = false; + Var.DefRanges = std::move(NonDerefDefRanges); + } + recordLocalVariable(std::move(Var), InlinedAt); } } @@ -1971,6 +2023,16 @@ return recordTypeIndexForDINode(Ty, TI, ClassTy); } +TypeIndex CodeViewDebug::getTypeIndexForReferenceTo(DITypeRef TypeRef) { + DIType *Ty = TypeRef.resolve(); + PointerRecord PR(getTypeIndex(Ty), + getPointerSizeInBytes() == 8 ? PointerKind::Near64 + : PointerKind::Near32, + PointerMode::LValueReference, PointerOptions::None, + Ty->getSizeInBits() / 8); + return TypeTable.writeKnownType(PR); +} + TypeIndex CodeViewDebug::getCompleteTypeIndex(DITypeRef TypeRef) { const DIType *Ty = TypeRef.resolve(); @@ -2078,7 +2140,8 @@ Flags |= LocalSymFlags::IsOptimizedOut; OS.AddComment("TypeIndex"); - TypeIndex TI = getCompleteTypeIndex(Var.DIVar->getType()); + TypeIndex TI = Var.Deref ? getTypeIndexForReferenceTo(Var.DIVar->getType()) + : getCompleteTypeIndex(Var.DIVar->getType()); OS.EmitIntValue(TI.getIndex(), 4); OS.AddComment("Flags"); OS.EmitIntValue(static_cast(Flags), 2); Index: llvm/test/CodeGen/X86/codeview-nrvo.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/X86/codeview-nrvo.ll @@ -0,0 +1,75 @@ +; RUN: llc -filetype=obj -O0 < %s | llvm-readobj -codeview | FileCheck %s + +; CHECK: LocalSym { +; CHECK-NEXT: Kind: S_LOCAL (0x113E) +; CHECK-NEXT: Type: string& +; CHECK-NEXT: Flags [ (0x0) +; CHECK-NEXT: ] +; CHECK-NEXT: VarName: Result +; CHECK-NEXT: } +; CHECK-NEXT: DefRangeRegisterRelSym { +; CHECK-NEXT: Kind: S_DEFRANGE_REGISTER_REL (0x1145) +; CHECK-NEXT: BaseRegister: 22 +; CHECK-NEXT: HasSpilledUDTMember: No +; CHECK-NEXT: OffsetInParent: 0 +; CHECK-NEXT: BasePointerOffset: {{[^0]}} + +target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32" +target triple = "i386-pc-windows-msvc19.0.24215" + +%struct.string = type { i32, i32, i8* } + +; Function Attrs: noinline nounwind optnone sspstrong +define void @"\01?fun@@YA?AUstring@@XZ"(%struct.string* noalias sret %agg.result) #0 !dbg !10 { +entry: + call void @llvm.dbg.declare(metadata %struct.string* %agg.result, metadata !21, metadata !22), !dbg !23 + %call = call dereferenceable(12) %struct.string* @"\01?getString@@YAAAUstring@@XZ"(), !dbg !23 + %0 = bitcast %struct.string* %agg.result to i8*, !dbg !23 + %1 = bitcast %struct.string* %call to i8*, !dbg !23 + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %0, i8* %1, i32 12, i32 4, i1 false), !dbg !23 + ret void, !dbg !24 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +declare dereferenceable(12) %struct.string* @"\01?getString@@YAAAUstring@@XZ"() #2 + +; Function Attrs: argmemonly nounwind +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i1) #3 + +attributes #0 = { noinline nounwind optnone sspstrong "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone speculatable } +attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { argmemonly nounwind } + +!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 6.0.0 ", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "codeview-nrvo.ll", directory: "C:\5Csrc\5Ctmp", checksumkind: CSK_MD5, checksum: "c547c362c610fa79e7abaddc76e1efe7") +!2 = !{} +!3 = !{!"/DEFAULTLIB:libcmt.lib"} +!4 = !{!"/DEFAULTLIB:oldnames.lib"} +!5 = !{i32 1, !"NumRegisterParameters", i32 0} +!6 = !{i32 2, !"CodeView", i32 1} +!7 = !{i32 2, !"Debug Info Version", i32 3} +!8 = !{i32 1, !"wchar_size", i32 2} +!9 = !{!"clang version 6.0.0 "} +!10 = distinct !DISubprogram(name: "fun", linkageName: "\01?fun@@YA?AUstring@@XZ", scope: !1, file: !1, line: 9, type: !11, isLocal: false, isDefinition: true, scopeLine: 9, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!11 = !DISubroutineType(types: !12) +!12 = !{!13} +!13 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "string", file: !1, line: 1, size: 96, elements: !14, identifier: ".?AUstring@@") +!14 = !{!15, !17, !18} +!15 = !DIDerivedType(tag: DW_TAG_member, name: "length", scope: !13, file: !1, line: 2, baseType: !16, size: 32) +!16 = !DIBasicType(name: "long int", size: 32, encoding: DW_ATE_signed) +!17 = !DIDerivedType(tag: DW_TAG_member, name: "size", scope: !13, file: !1, line: 3, baseType: !16, size: 32, offset: 32) +!18 = !DIDerivedType(tag: DW_TAG_member, name: "data", scope: !13, file: !1, line: 4, baseType: !19, size: 32, offset: 64) +!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !20, size: 32) +!20 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!21 = !DILocalVariable(name: "Result", scope: !10, file: !1, line: 10, type: !13) +!22 = !DIExpression() +!23 = !DILocation(line: 10, scope: !10) +!24 = !DILocation(line: 11, scope: !10)