diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1164,7 +1164,7 @@
Flags<[NoXarchOption, RenderAsInput]>,
HelpText<"Directory to output dSYM's (if any) to">, MetaVarName<"
">;
// GCC style -dumpdir. We intentionally don't implement the less useful -dumpbase{,-ext}.
-def dumpdir : Separate<["-"], "dumpdir">, Flags<[CC1Option]>,
+def dumpdir : Separate<["-"], "dumpdir">, Flags<[CC1Option, CoreOption]>,
MetaVarName<"">,
HelpText<"Use as a prefix to form auxiliary and dump file names">;
def dumpmachine : Flag<["-"], "dumpmachine">;
@@ -3436,11 +3436,13 @@
CodeGenOpts<"DebugColumnInfo">, DefaultTrue,
NegFlag, PosFlag, BothFlags<[CoreOption]>>,
Group;
-def gsplit_dwarf : Flag<["-"], "gsplit-dwarf">, Group;
+def gsplit_dwarf : Flag<["-"], "gsplit-dwarf">, Group,
+ Flags<[CoreOption]>;
def gsplit_dwarf_EQ : Joined<["-"], "gsplit-dwarf=">, Group,
- HelpText<"Set DWARF fission mode">,
+ Flags<[CoreOption]>, HelpText<"Set DWARF fission mode">,
Values<"split,single">;
-def gno_split_dwarf : Flag<["-"], "gno-split-dwarf">, Group;
+def gno_split_dwarf : Flag<["-"], "gno-split-dwarf">, Group,
+ Flags<[CoreOption]>;
def gsimple_template_names : Flag<["-"], "gsimple-template-names">, Group;
def gsimple_template_names_EQ
: Joined<["-"], "gsimple-template-names=">,
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3895,12 +3895,13 @@
// `-dumpdir x-` to cc1. If -o is unspecified, use
// stem(getDefaultImageName()) (usually stem("a.out") = "a").
if (!Args.hasArg(options::OPT_dumpdir)) {
+ Arg *FinalOutput = Args.getLastArg(options::OPT_o, options::OPT__SLASH_o);
Arg *Arg = Args.MakeSeparateArg(
nullptr, getOpts().getOption(options::OPT_dumpdir),
- Args.MakeArgString(Args.getLastArgValue(
- options::OPT_o,
- llvm::sys::path::stem(getDefaultImageName())) +
- "-"));
+ Args.MakeArgString(
+ (FinalOutput ? FinalOutput->getValue()
+ : llvm::sys::path::stem(getDefaultImageName())) +
+ "-"));
Arg->claim();
Args.append(Arg);
}
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5645,7 +5645,8 @@
// can propagate it to the backend.
bool SplitDWARF = (DwarfFission != DwarfFissionKind::None) &&
(TC.getTriple().isOSBinFormatELF() ||
- TC.getTriple().isOSBinFormatWasm()) &&
+ TC.getTriple().isOSBinFormatWasm() ||
+ TC.getTriple().isOSBinFormatCOFF()) &&
(isa(JA) || isa(JA) ||
isa(JA));
if (SplitDWARF) {
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1284,7 +1284,7 @@
if (const Arg *A = Args.getLastArg(options::OPT_dumpdir)) {
T = A->getValue();
} else {
- Arg *FinalOutput = Args.getLastArg(options::OPT_o);
+ Arg *FinalOutput = Args.getLastArg(options::OPT_o, options::OPT__SLASH_o);
if (FinalOutput && Args.hasArg(options::OPT_c)) {
T = FinalOutput->getValue();
llvm::sys::path::remove_filename(T);
diff --git a/clang/test/Driver/split-debug.c b/clang/test/Driver/split-debug.c
--- a/clang/test/Driver/split-debug.c
+++ b/clang/test/Driver/split-debug.c
@@ -16,6 +16,7 @@
// RUN: %clang -### -c -target wasm32 -gsplit-dwarf -g %s 2>&1 | FileCheck %s --check-prefix=SPLIT
// RUN: %clang -### -c -target amdgcn-amd-amdhsa -gsplit-dwarf -g %s 2>&1 | FileCheck %s --check-prefix=SPLIT
+// RUN: %clang_cl -### -c --target x86_64-unknown-windows-msvc -gsplit-dwarf -g %s 2>&1 | FileCheck %s --check-prefix=SPLIT
/// -gsplit-dwarf is a no-op on a non-ELF platform.
// RUN: %clang -### -c -target x86_64-apple-darwin -gsplit-dwarf -g %s 2>&1 | FileCheck %s --check-prefix=DARWIN
@@ -51,12 +52,14 @@
// SINGLE-NOT: "-split-dwarf-output"
// RUN: %clang -### -c -target x86_64 -gsplit-dwarf=single -g -o %tfoo.o %s 2>&1 | FileCheck %s --check-prefix=SINGLE_WITH_FILENAME
+// RUN: %clang_cl -### -c --target x86_64-unknown-windows-msvc -gsplit-dwarf=single -g -o %tfoo.o %s 2>&1 | FileCheck %s --check-prefix=SINGLE_WITH_FILENAME
// SINGLE_WITH_FILENAME: "-split-dwarf-file" "{{.*}}foo.o"
// SINGLE_WITH_FILENAME-NOT: "-split-dwarf-output"
/// If linking is the final phase, the .dwo filename is derived from -o (if specified) or "a".
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -gsplit-dwarf -g %s -o obj/out 2>&1 | FileCheck %s --check-prefix=SPLIT_LINK
+// RUN: %clang_cl -### --target=x86_64-unknown-windows-msvc -gsplit-dwarf -g %s -o obj/out 2>&1 | FileCheck %s --check-prefix=SPLIT_LINK
// RUN: %clang -### --target=x86_64-unknown-linux-gnu -gsplit-dwarf -g %s 2>&1 | FileCheck %s --check-prefix=SPLIT_LINK_A
// SPLIT_LINK: "-dumpdir" "obj/out-"
diff --git a/llvm/include/llvm/MC/MCWinCOFFObjectWriter.h b/llvm/include/llvm/MC/MCWinCOFFObjectWriter.h
--- a/llvm/include/llvm/MC/MCWinCOFFObjectWriter.h
+++ b/llvm/include/llvm/MC/MCWinCOFFObjectWriter.h
@@ -51,6 +51,10 @@
std::unique_ptr
createWinCOFFObjectWriter(std::unique_ptr MOTW,
raw_pwrite_stream &OS);
+
+std::unique_ptr
+createWinCOFFDwoObjectWriter(std::unique_ptr MOTW,
+ raw_pwrite_stream &OS, raw_pwrite_stream &DwoOS);
} // end namespace llvm
#endif // LLVM_MC_MCWINCOFFOBJECTWRITER_H
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
--- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
@@ -39,6 +39,7 @@
#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Object/COFF.h"
#include "llvm/Object/Decompressor.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/ObjectFile.h"
@@ -47,8 +48,8 @@
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Format.h"
-#include "llvm/Support/LEB128.h"
#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/LEB128.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
@@ -1887,9 +1888,15 @@
S.Data = Data;
}
- if (RelocatedSection != Obj.section_end() && Name.contains(".dwo"))
+ if (RelocatedSection != Obj.section_end() && Name.contains(".dwo")) {
+ // Unlike ELF, for each section in COFF, RelocatedSection is itself so
+ // it always exists.
+ if (isa(&Obj) && Section.relocations().empty())
+ continue;
+
HandleWarning(
createError("Unexpected relocations for dwo section " + Name));
+ }
if (RelocatedSection == Obj.section_end() ||
(RelocAction == DWARFContext::ProcessDebugRelocations::Ignore))
diff --git a/llvm/lib/MC/MCAsmBackend.cpp b/llvm/lib/MC/MCAsmBackend.cpp
--- a/llvm/lib/MC/MCAsmBackend.cpp
+++ b/llvm/lib/MC/MCAsmBackend.cpp
@@ -68,8 +68,11 @@
case Triple::Wasm:
return createWasmDwoObjectWriter(
cast(std::move(TW)), OS, DwoOS);
+ case Triple::COFF:
+ return createWinCOFFDwoObjectWriter(
+ cast(std::move(TW)), OS, DwoOS);
default:
- report_fatal_error("dwo only supported with ELF and Wasm");
+ report_fatal_error("dwo only supported with ELF Wasm and COFF");
}
}
diff --git a/llvm/lib/MC/WinCOFFObjectWriter.cpp b/llvm/lib/MC/WinCOFFObjectWriter.cpp
--- a/llvm/lib/MC/WinCOFFObjectWriter.cpp
+++ b/llvm/lib/MC/WinCOFFObjectWriter.cpp
@@ -121,6 +121,10 @@
SmallVector OffsetSymbols;
};
+bool isDwoSection(const MCSection &Sec) {
+ return Sec.getName().endswith(".dwo");
+}
+
class WinCOFFObjectWriter;
class WinCOFFWriter {
@@ -154,7 +158,14 @@
MCSectionCOFF *AddrsigSection = nullptr;
MCSectionCOFF *CGProfileSection = nullptr;
- WinCOFFWriter(WinCOFFObjectWriter &OWriter, raw_pwrite_stream &OS);
+ enum DwoMode {
+ AllSections,
+ NonDwoOnly,
+ DwoOnly,
+ } Mode;
+
+ WinCOFFWriter(WinCOFFObjectWriter &OWriter, raw_pwrite_stream &OS,
+ DwoMode Mode);
void reset();
void executePostLayoutBinding(MCAssembler &Asm, const MCAsmLayout &Layout);
@@ -200,13 +211,22 @@
friend class WinCOFFWriter;
std::unique_ptr TargetObjectWriter;
- std::unique_ptr ObjWriter;
+ std::unique_ptr ObjWriter, DwoWriter;
public:
WinCOFFObjectWriter(std::unique_ptr MOTW,
raw_pwrite_stream &OS)
: TargetObjectWriter(std::move(MOTW)),
- ObjWriter(std::make_unique(*this, OS)) {}
+ ObjWriter(std::make_unique(*this, OS,
+ WinCOFFWriter::AllSections)) {
+ }
+ WinCOFFObjectWriter(std::unique_ptr MOTW,
+ raw_pwrite_stream &OS, raw_pwrite_stream &DwoOS)
+ : TargetObjectWriter(std::move(MOTW)),
+ ObjWriter(std::make_unique(*this, OS,
+ WinCOFFWriter::NonDwoOnly)),
+ DwoWriter(std::make_unique(*this, DwoOS,
+ WinCOFFWriter::DwoOnly)) {}
// MCObjectWriter interface implementation.
void reset() override;
@@ -239,8 +259,8 @@
// WinCOFFWriter class implementation
WinCOFFWriter::WinCOFFWriter(WinCOFFObjectWriter &OWriter,
- raw_pwrite_stream &OS)
- : OWriter(OWriter), W(OS, support::little) {
+ raw_pwrite_stream &OS, DwoMode Mode)
+ : OWriter(OWriter), W(OS, support::little), Mode(Mode) {
Header.Machine = OWriter.TargetObjectWriter->getMachine();
// Some relocations on ARM64 (the 21 bit ADRP relocations) have a slightly
// limited range for the immediate offset (+/- 1 MB); create extra offset
@@ -818,12 +838,18 @@
const MCAsmLayout &Layout) {
// "Define" each section & symbol. This creates section & symbol
// entries in the staging area.
- for (const auto &Section : Asm)
+ for (const auto &Section : Asm) {
+ if ((Mode == NonDwoOnly && isDwoSection(Section)) ||
+ (Mode == DwoOnly && !isDwoSection(Section)))
+ continue;
defineSection(static_cast(Section), Layout);
+ }
- for (const MCSymbol &Symbol : Asm.symbols())
- if (!Symbol.isTemporary())
- DefineSymbol(Symbol, Asm, Layout);
+ if (Mode != DwoOnly) {
+ for (const MCSymbol &Symbol : Asm.symbols())
+ if (!Symbol.isTemporary())
+ DefineSymbol(Symbol, Asm, Layout);
+ }
}
void WinCOFFWriter::recordRelocation(MCAssembler &Asm,
@@ -998,7 +1024,8 @@
setWeakDefaultNames();
assignSectionNumbers();
- createFileSymbols(Asm);
+ if (Mode != DwoOnly)
+ createFileSymbols(Asm);
for (auto &Symbol : Symbols) {
// Update section number & offset for symbols that have them.
@@ -1068,7 +1095,7 @@
}
// Create the contents of the .llvm_addrsig section.
- if (OWriter.EmitAddrsigSection) {
+ if (Mode != DwoOnly && OWriter.EmitAddrsigSection) {
auto Frag = new MCDataFragment(AddrsigSection);
Frag->setLayoutOrder(0);
raw_svector_ostream OS(Frag->getContents());
@@ -1089,7 +1116,7 @@
}
// Create the contents of the .llvm.call-graph-profile section.
- if (CGProfileSection) {
+ if (Mode != DwoOnly && CGProfileSection) {
auto *Frag = new MCDataFragment(CGProfileSection);
Frag->setLayoutOrder(0);
raw_svector_ostream OS(Frag->getContents());
@@ -1122,8 +1149,12 @@
sections::iterator IE = Sections.end();
MCAssembler::iterator J = Asm.begin();
MCAssembler::iterator JE = Asm.end();
- for (; I != IE && J != JE; ++I, ++J)
- assert((**I).MCSection == &*J && "Wrong bound MCSection");
+ for (; I != IE && J != JE; ++I, ++J) {
+ while (J != JE && ((Mode == NonDwoOnly && isDwoSection(*J)) ||
+ (Mode == DwoOnly && !isDwoSection(*J))))
+ J++;
+ assert(J != JE && (**I).MCSection == &*J && "Wrong bound MCSection");
+ }
#endif
// Write section contents.
@@ -1152,6 +1183,8 @@
void WinCOFFObjectWriter::reset() {
ObjWriter->reset();
+ if (DwoWriter)
+ DwoWriter->reset();
MCObjectWriter::reset();
}
@@ -1188,6 +1221,8 @@
}
ObjWriter->executePostLayoutBinding(Asm, Layout);
+ if (DwoWriter)
+ DwoWriter->executePostLayoutBinding(Asm, Layout);
}
void WinCOFFObjectWriter::recordRelocation(MCAssembler &Asm,
@@ -1195,13 +1230,19 @@
const MCFragment *Fragment,
const MCFixup &Fixup, MCValue Target,
uint64_t &FixedValue) {
+ assert(!isDwoSection(*Fragment->getParent()) &&
+ "No relocation in Dwo sections");
ObjWriter->recordRelocation(Asm, Layout, Fragment, Fixup, Target, FixedValue);
}
uint64_t WinCOFFObjectWriter::writeObject(MCAssembler &Asm,
const MCAsmLayout &Layout) {
- return ObjWriter->writeObject(Asm, Layout);
+ uint64_t TotalSize = ObjWriter->writeObject(Asm, Layout);
+ if (DwoWriter)
+ TotalSize += DwoWriter->writeObject(Asm, Layout);
+ return TotalSize;
}
+
MCWinCOFFObjectTargetWriter::MCWinCOFFObjectTargetWriter(unsigned Machine_)
: Machine(Machine_) {}
@@ -1215,3 +1256,9 @@
std::unique_ptr MOTW, raw_pwrite_stream &OS) {
return std::make_unique(std::move(MOTW), OS);
}
+
+std::unique_ptr llvm::createWinCOFFDwoObjectWriter(
+ std::unique_ptr MOTW, raw_pwrite_stream &OS,
+ raw_pwrite_stream &DwoOS) {
+ return std::make_unique(std::move(MOTW), OS, DwoOS);
+}
diff --git a/llvm/test/DebugInfo/COFF/dwarf-headers.ll b/llvm/test/DebugInfo/COFF/dwarf-headers.ll
new file mode 100644
--- /dev/null
+++ b/llvm/test/DebugInfo/COFF/dwarf-headers.ll
@@ -0,0 +1,75 @@
+; RUN: llc -dwarf-version=4 \
+; RUN: -filetype=obj -O0 -mtriple=x86_64-unknown-windows-msvc < %s \
+; RUN: | llvm-dwarfdump -v - | FileCheck %s --check-prefix=SINGLE-4
+
+; RUN: llc -split-dwarf-file=foo.dwo -split-dwarf-output=%t.dwo \
+; RUN: -dwarf-version=4 \
+; RUN: -filetype=obj -O0 -mtriple=x86_64-unknown-windows-msvc < %s \
+; RUN: | llvm-dwarfdump -v - | FileCheck %s --check-prefix=O-4
+; RUN: llvm-dwarfdump -v %t.dwo | FileCheck %s --check-prefix=DWO-4
+
+; This test is derived from test/CodeGen/X86/dwarf-headers.ll
+
+; Looking for DWARF headers to be generated correctly.
+; There are 8 variants with 5 formats: v4 CU, v4 TU, v5 normal/partial CU,
+; v5 skeleton/split CU, v5 normal/split TU. Some v5 variants differ only
+; in the unit_type code, and the skeleton/split CU differs from normal/partial
+; by having one extra field (dwo_id).
+; (v2 thru v4 CUs are all the same, and TUs were invented in v4,
+; so we don't bother checking older versions.)
+
+; Test case built from:
+;struct S {
+; int s1;
+;};
+;
+;S s;
+
+; Verify the v4 non-split headers.
+; Note that we check the exact offset of the DIEs because that tells us
+; the length of the header.
+;
+; SINGLE-4: .debug_info contents:
+; SINGLE-4: 0x00000000: Compile Unit: {{.*}} version = 0x0004, abbr_offset
+; SINGLE-4: 0x0000000b: DW_TAG_compile_unit
+
+; Verify the v4 split headers.
+;
+; O-4: .debug_info contents:
+; O-4: 0x00000000: Compile Unit: {{.*}} version = 0x0004, abbr_offset
+; O-4: 0x0000000b: DW_TAG_compile_unit
+;
+; DWO-4: .debug_info.dwo contents:
+; DWO-4: 0x00000000: Compile Unit: {{.*}} version = 0x0004, abbr_offset
+; DWO-4: 0x0000000b: DW_TAG_compile_unit
+
+
+; ModuleID = 't.cpp'
+source_filename = "t.cpp"
+target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-windows-msvc"
+
+%struct.S = type { i32 }
+
+@"?s@@3US@@A" = dso_local global %struct.S zeroinitializer, align 4, !dbg !0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!9, !10, !11, !12, !13, !14}
+!llvm.ident = !{!15}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "s", linkageName: "?s@@3US@@A", scope: !2, file: !3, line: 5, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git f1106ef6c9d14d5b516ec352279aeee8f9d12818)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "t.cpp", directory: "e:\\llvm-project\\foo")
+!4 = !{!0}
+!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !3, line: 1, size: 32, flags: DIFlagTypePassByValue, elements: !6, identifier: ".?AUS@@")
+!6 = !{!7}
+!7 = !DIDerivedType(tag: DW_TAG_member, name: "s1", scope: !5, file: !3, line: 2, baseType: !8, size: 32)
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = !{i32 7, !"Dwarf Version", i32 4}
+!10 = !{i32 2, !"Debug Info Version", i32 3}
+!11 = !{i32 1, !"wchar_size", i32 2}
+!12 = !{i32 8, !"PIC Level", i32 2}
+!13 = !{i32 7, !"uwtable", i32 2}
+!14 = !{i32 1, !"MaxTLSAlign", i32 65536}
+!15 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git f1106ef6c9d14d5b516ec352279aeee8f9d12818)"}
diff --git a/llvm/test/DebugInfo/COFF/fission-cu.ll b/llvm/test/DebugInfo/COFF/fission-cu.ll
new file mode 100644
--- /dev/null
+++ b/llvm/test/DebugInfo/COFF/fission-cu.ll
@@ -0,0 +1,120 @@
+; RUN: llc -split-dwarf-file=baz.dwo -O0 %s -mtriple=x86_64-unknown-windows-msvc -filetype=obj -o %t
+; RUN: llvm-dwarfdump -v -all %t | FileCheck %s
+; RUN: llvm-readobj --relocations %t | FileCheck --check-prefix=OBJ %s
+; RUN: llvm-objdump -h %t | FileCheck --check-prefix=HDR %s
+
+; This test is derived from test/DebugInfo/X86/fission-cu.ll
+
+source_filename = "test/DebugInfo/X86/fission-cu.ll"
+
+@a = common global i32 0, align 4, !dbg !0
+
+!llvm.dbg.cu = !{!4}
+!llvm.module.flags = !{!7}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = !DIGlobalVariable(name: "a", scope: null, file: !2, line: 1, type: !3, isLocal: false, isDefinition: true)
+!2 = !DIFile(filename: "baz.c", directory: "e:\\llvm-project\\tmp")
+!3 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
+!4 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang version 3.3 (trunk 169021) (llvm/trunk 169020)", isOptimized: false, runtimeVersion: 0, splitDebugFilename: "baz.dwo", emissionKind: FullDebug, enums: !5, retainedTypes: !5, globals: !6, imports: !5)
+!5 = !{}
+; Check that the skeleton compile unit contains the proper attributes:
+; This DIE has the following attributes: DW_AT_comp_dir, DW_AT_stmt_list,
+; DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, DW_AT_dwo_name, DW_AT_dwo_id,
+; DW_AT_ranges_base, DW_AT_addr_base.
+
+; CHECK: .debug_abbrev contents:
+; CHECK: Abbrev table for offset: 0x00000000
+; CHECK: [1] DW_TAG_compile_unit DW_CHILDREN_no
+; CHECK: DW_AT_stmt_list DW_FORM_sec_offset
+; CHECK: DW_AT_comp_dir DW_FORM_strp
+; CHECK: DW_AT_GNU_dwo_name DW_FORM_strp
+; CHECK: DW_AT_GNU_dwo_id DW_FORM_data8
+
+; Check that we're using the right forms.
+; CHECK: .debug_abbrev.dwo contents:
+; CHECK: Abbrev table for offset: 0x00000000
+; CHECK: [1] DW_TAG_compile_unit DW_CHILDREN_yes
+; CHECK: DW_AT_producer DW_FORM_GNU_str_index
+; CHECK: DW_AT_language DW_FORM_data2
+; CHECK: DW_AT_name DW_FORM_GNU_str_index
+; CHECK: DW_AT_GNU_dwo_name DW_FORM_GNU_str_index
+; CHECK-NOT: DW_AT_low_pc
+; CHECK-NOT: DW_AT_stmt_list
+; CHECK-NOT: DW_AT_comp_dir
+; CHECK: DW_AT_GNU_dwo_id DW_FORM_data8
+
+; CHECK: [2] DW_TAG_variable DW_CHILDREN_no
+; CHECK: DW_AT_name DW_FORM_GNU_str_index
+; CHECK: DW_AT_type DW_FORM_ref4
+; CHECK: DW_AT_external DW_FORM_flag_present
+; CHECK: DW_AT_decl_file DW_FORM_data1
+; CHECK: DW_AT_decl_line DW_FORM_data1
+; CHECK: DW_AT_location DW_FORM_exprloc
+
+; CHECK: [3] DW_TAG_base_type DW_CHILDREN_no
+; CHECK: DW_AT_name DW_FORM_GNU_str_index
+; CHECK: DW_AT_encoding DW_FORM_data1
+; CHECK: DW_AT_byte_size DW_FORM_data1
+
+; CHECK: .debug_info contents:
+; CHECK: DW_TAG_compile_unit
+; CHECK-NEXT: DW_AT_stmt_list [DW_FORM_sec_offset] (0x00000000)
+; CHECK-NEXT: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x00000000] = "e:\\llvm-project\\tmp")
+; CHECK-NEXT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x00000014] = "baz.dwo")
+; CHECK-NEXT: DW_AT_GNU_dwo_id [DW_FORM_data8] (0x1f1f859683d49324)
+
+; Check that the rest of the compile units have information.
+; CHECK: .debug_info.dwo contents:
+; CHECK: DW_TAG_compile_unit
+; CHECK: DW_AT_producer [DW_FORM_GNU_str_index] (indexed (00000002) string = "clang version 3.3 (trunk 169021) (llvm/trunk 169020)")
+; CHECK: DW_AT_language [DW_FORM_data2] (DW_LANG_C99)
+; CHECK: DW_AT_name [DW_FORM_GNU_str_index] (indexed (00000003) string = "baz.c")
+; CHECK: DW_AT_GNU_dwo_name [DW_FORM_GNU_str_index] (indexed (00000004) string = "baz.dwo")
+; CHECK-NOT: DW_AT_low_pc
+; CHECK-NOT: DW_AT_stmt_list
+; CHECK-NOT: DW_AT_comp_dir
+; CHECK: DW_AT_GNU_dwo_id [DW_FORM_data8] (0x1f1f859683d49324)
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_name [DW_FORM_GNU_str_index] (indexed (00000000) string = "a")
+; CHECK: DW_AT_type [DW_FORM_ref4] (cu + 0x{{[0-9a-f]*}} => {[[TYPE:0x[0-9a-f]*]]}
+; CHECK: DW_AT_external [DW_FORM_flag_present] (true)
+; CHECK: DW_AT_decl_file [DW_FORM_data1] (0x01)
+; CHECK: DW_AT_decl_line [DW_FORM_data1] (1)
+; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_GNU_addr_index 0x0)
+; CHECK: [[TYPE]]: DW_TAG_base_type
+; CHECK: DW_AT_name [DW_FORM_GNU_str_index] (indexed (00000001) string = "int")
+
+; CHECK: .debug_str contents:
+; CHECK: 0x00000000: "e:\\llvm-project\\tmp"
+; CHECK: 0x00000014: "baz.dwo"
+
+; CHECK: .debug_str.dwo contents:
+; CHECK: 0x00000000: "a"
+; CHECK: 0x00000002: "int"
+; CHECK: 0x00000006: "clang version 3.3 (trunk 169021) (llvm/trunk 169020)"
+; CHECK: 0x0000003b: "baz.c"
+; CHECK: 0x00000041: "baz.dwo"
+
+; CHECK: .debug_str_offsets.dwo contents:
+; CHECK: 0x00000000: 00000000
+; CHECK: 0x00000004: 00000002
+; CHECK: 0x00000008: 00000006
+; CHECK: 0x0000000c: 0000003b
+; CHECK: 0x00000010: 00000041
+
+; Object file checks
+; For COFF we should have this set of relocations for the debug info section
+;
+; OBJ: .debug_info
+; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_abbrev (6)
+; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_line (26)
+; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_str (10)
+; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_str (10)
+; OBJ-NEXT: IMAGE_REL_AMD64_SECREL .debug_addr (20)
+; OBJ-NEXT: }
+
+; HDR-NOT: .debug_aranges
+
+!6 = !{!0}
+!7 = !{i32 1, !"Debug Info Version", i32 3}
diff --git a/llvm/test/DebugInfo/COFF/fission-sections.ll b/llvm/test/DebugInfo/COFF/fission-sections.ll
new file mode 100644
--- /dev/null
+++ b/llvm/test/DebugInfo/COFF/fission-sections.ll
@@ -0,0 +1,42 @@
+; RUN: llc -split-dwarf-file=baz.dwo -split-dwarf-output=%t.dwo -O0 %s -mtriple=x86_64-unknown-windows-msvc -filetype=obj -o %t
+; RUN: llvm-objdump -h %t | FileCheck --check-prefix=OBJ %s
+; RUN: llvm-objdump -h %t.dwo | FileCheck --check-prefix=DWO %s
+
+; This test is derived from test/DebugInfo/X86/fission-cu.ll
+; But it checks that the output objects have the expected sections
+
+source_filename = "test/DebugInfo/X86/fission-cu.ll"
+
+@a = common global i32 0, align 4, !dbg !0
+
+!llvm.dbg.cu = !{!4}
+!llvm.module.flags = !{!7}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = !DIGlobalVariable(name: "a", scope: null, file: !2, line: 1, type: !3, isLocal: false, isDefinition: true)
+!2 = !DIFile(filename: "baz.c", directory: "e:\\llvm-project\\tmp")
+!3 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
+!4 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang version 3.3 (trunk 169021) (llvm/trunk 169020)", isOptimized: false, runtimeVersion: 0, splitDebugFilename: "baz.dwo", emissionKind: FullDebug, enums: !5, retainedTypes: !5, globals: !6, imports: !5)
+!5 = !{}
+!6 = !{!0}
+!7 = !{i32 1, !"Debug Info Version", i32 3}
+
+; CHECK-LABEL: Sections:
+
+; OBJ: Idx Name
+; OBJ-NEXT: 0 .text
+; OBJ-NEXT: 1 .data
+; OBJ-NEXT: 2 .bss
+; OBJ-NEXT: 3 .debug_abbrev
+; OBJ-NEXT: 4 .debug_info
+; OBJ-NEXT: 5 .debug_str
+; OBJ-NEXT: 6 .debug_addr
+; OBJ-NEXT: 7 .debug_pubnames
+; OBJ-NEXT: 8 .debug_pubtypes
+; OBJ-NEXT: 9 .debug_line
+
+; DWO: Idx Name
+; DWO-NEXT: 0 .debug_str.dwo
+; DWO-NEXT: 1 .debug_str_offsets.dwo
+; DWO-NEXT: 2 .debug_info.dwo
+; DWO-NEXT: 3 .debug_abbrev.dwo