aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/Extensions.rst38
-rw-r--r--include/llvm/MC/MCContext.h8
-rw-r--r--include/llvm/MC/MCSectionCOFF.h14
-rw-r--r--lib/CodeGen/TargetLoweringObjectFileImpl.cpp3
-rw-r--r--lib/MC/MCContext.cpp44
-rw-r--r--lib/MC/MCParser/COFFAsmParser.cpp106
-rw-r--r--lib/MC/WinCOFFStreamer.cpp3
-rw-r--r--test/MC/COFF/section-comdat.s188
8 files changed, 355 insertions, 49 deletions
diff --git a/docs/Extensions.rst b/docs/Extensions.rst
index 925d727..e308dbc 100644
--- a/docs/Extensions.rst
+++ b/docs/Extensions.rst
@@ -105,3 +105,41 @@ Supported COMDAT types:
.section .xdata$foo
.linkonce associative .text$foo
...
+
+``.section`` Directive
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+MC supports passing the information in ``.linkonce`` at the end of
+``.section``. For example, these two codes are equivalent
+
+.. code-block:: gas
+
+ .section secName, "dr", discard, "Symbol1"
+ .globl Symbol1
+ Symbol1:
+ .long 1
+
+.. code-block:: gas
+
+ .section secName, "dr"
+ .linkonce discard
+ .globl Symbol1
+ Symbol1:
+ .long 1
+
+Note that in the combined form the COMDAT symbol is explict. This
+extension exits to support multiple sections with the same name in
+different comdats:
+
+
+.. code-block:: gas
+
+ .section secName, "dr", discard, "Symbol1"
+ .globl Symbol1
+ Symbol1:
+ .long 1
+
+ .section secName, "dr", discard, "Symbol2"
+ .globl Symbol2
+ Symbol2:
+ .long 1
diff --git a/include/llvm/MC/MCContext.h b/include/llvm/MC/MCContext.h
index 8fba18c..c8b6626 100644
--- a/include/llvm/MC/MCContext.h
+++ b/include/llvm/MC/MCContext.h
@@ -258,9 +258,15 @@ namespace llvm {
const MCSectionCOFF *getCOFFSection(StringRef Section,
unsigned Characteristics,
- SectionKind Kind, int Selection = 0,
+ SectionKind Kind,
+ StringRef COMDATSymName,
+ int Selection,
const MCSectionCOFF *Assoc = 0);
+ const MCSectionCOFF *getCOFFSection(StringRef Section,
+ unsigned Characteristics,
+ SectionKind Kind);
+
const MCSectionCOFF *getCOFFSection(StringRef Section);
/// @}
diff --git a/include/llvm/MC/MCSectionCOFF.h b/include/llvm/MC/MCSectionCOFF.h
index 754e829..45c84ae 100644
--- a/include/llvm/MC/MCSectionCOFF.h
+++ b/include/llvm/MC/MCSectionCOFF.h
@@ -19,6 +19,7 @@
#include "llvm/Support/COFF.h"
namespace llvm {
+class MCSymbol;
/// MCSectionCOFF - This represents a section on Windows
class MCSectionCOFF : public MCSection {
@@ -32,6 +33,11 @@ namespace llvm {
/// drawn from the enums below.
mutable unsigned Characteristics;
+ /// The COMDAT symbol of this section. Only valid if this is a COMDAT
+ /// section. Two COMDAT sections are merged if they have the same
+ /// COMDAT symbol.
+ const MCSymbol *COMDATSymbol;
+
/// Selection - This is the Selection field for the section symbol, if
/// it is a COMDAT section (Characteristics & IMAGE_SCN_LNK_COMDAT) != 0
mutable int Selection;
@@ -44,9 +50,11 @@ namespace llvm {
private:
friend class MCContext;
MCSectionCOFF(StringRef Section, unsigned Characteristics,
- int Selection, const MCSectionCOFF *Assoc, SectionKind K)
- : MCSection(SV_COFF, K), SectionName(Section),
- Characteristics(Characteristics), Selection(Selection), Assoc(Assoc) {
+ const MCSymbol *COMDATSymbol, int Selection,
+ const MCSectionCOFF *Assoc, SectionKind K)
+ : MCSection(SV_COFF, K), SectionName(Section),
+ Characteristics(Characteristics), COMDATSymbol(COMDATSymbol),
+ Selection(Selection), Assoc(Assoc) {
assert ((Characteristics & 0x00F00000) == 0 &&
"alignment must not be set upon section creation");
assert ((Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) ==
diff --git a/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 66ba3ec..59d7b57 100644
--- a/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -733,6 +733,7 @@ getExplicitSectionGlobal(const GlobalValue *GV, SectionKind Kind,
return getContext().getCOFFSection(Name,
Characteristics,
Kind,
+ "",
Selection);
}
@@ -768,7 +769,7 @@ SelectSectionForGlobal(const GlobalValue *GV, SectionKind Kind,
Characteristics |= COFF::IMAGE_SCN_LNK_COMDAT;
return getContext().getCOFFSection(Name.str(), Characteristics,
- Kind, COFF::IMAGE_COMDAT_SELECT_ANY);
+ Kind, "", COFF::IMAGE_COMDAT_SELECT_ANY);
}
if (Kind.isText())
diff --git a/lib/MC/MCContext.cpp b/lib/MC/MCContext.cpp
index 67904f6..3b45d16 100644
--- a/lib/MC/MCContext.cpp
+++ b/lib/MC/MCContext.cpp
@@ -34,8 +34,7 @@ typedef std::pair<std::string, std::string> SectionGroupPair;
typedef StringMap<const MCSectionMachO*> MachOUniqueMapTy;
typedef std::map<SectionGroupPair, const MCSectionELF *> ELFUniqueMapTy;
-typedef StringMap<const MCSectionCOFF*> COFFUniqueMapTy;
-
+typedef std::map<SectionGroupPair, const MCSectionCOFF *> COFFUniqueMapTy;
MCContext::MCContext(const MCAsmInfo *mai, const MCRegisterInfo *mri,
const MCObjectFileInfo *mofi, const SourceMgr *mgr,
@@ -280,32 +279,51 @@ const MCSectionELF *MCContext::CreateELFGroupSection() {
return Result;
}
-const MCSectionCOFF *MCContext::getCOFFSection(StringRef Section,
- unsigned Characteristics,
- SectionKind Kind, int Selection,
- const MCSectionCOFF *Assoc) {
+const MCSectionCOFF *
+MCContext::getCOFFSection(StringRef Section, unsigned Characteristics,
+ SectionKind Kind, StringRef COMDATSymName,
+ int Selection, const MCSectionCOFF *Assoc) {
if (COFFUniquingMap == 0)
COFFUniquingMap = new COFFUniqueMapTy();
COFFUniqueMapTy &Map = *(COFFUniqueMapTy*)COFFUniquingMap;
// Do the lookup, if we have a hit, return it.
- StringMapEntry<const MCSectionCOFF*> &Entry = Map.GetOrCreateValue(Section);
- if (Entry.getValue()) return Entry.getValue();
- MCSectionCOFF *Result = new (*this) MCSectionCOFF(Entry.getKey(),
- Characteristics,
- Selection, Assoc, Kind);
+ SectionGroupPair P(Section, COMDATSymName);
+ std::pair<COFFUniqueMapTy::iterator, bool> Entry =
+ Map.insert(std::make_pair(P, (MCSectionCOFF *)0));
+ COFFUniqueMapTy::iterator Iter = Entry.first;
+ if (!Entry.second)
+ return Iter->second;
+
+ const MCSymbol *COMDATSymbol = NULL;
+ if (!COMDATSymName.empty())
+ COMDATSymbol = GetOrCreateSymbol(COMDATSymName);
+
+ MCSectionCOFF *Result =
+ new (*this) MCSectionCOFF(Iter->first.first, Characteristics,
+ COMDATSymbol, Selection, Assoc, Kind);
- Entry.setValue(Result);
+ Iter->second = Result;
return Result;
}
+const MCSectionCOFF *
+MCContext::getCOFFSection(StringRef Section, unsigned Characteristics,
+ SectionKind Kind) {
+ return getCOFFSection(Section, Characteristics, Kind, "", 0);
+}
+
const MCSectionCOFF *MCContext::getCOFFSection(StringRef Section) {
if (COFFUniquingMap == 0)
COFFUniquingMap = new COFFUniqueMapTy();
COFFUniqueMapTy &Map = *(COFFUniqueMapTy*)COFFUniquingMap;
- return Map.lookup(Section);
+ SectionGroupPair P(Section, "");
+ COFFUniqueMapTy::iterator Iter = Map.find(P);
+ if (Iter == Map.end())
+ return 0;
+ return Iter->second;
}
//===----------------------------------------------------------------------===//
diff --git a/lib/MC/MCParser/COFFAsmParser.cpp b/lib/MC/MCParser/COFFAsmParser.cpp
index df1794c..d8343a3 100644
--- a/lib/MC/MCParser/COFFAsmParser.cpp
+++ b/lib/MC/MCParser/COFFAsmParser.cpp
@@ -35,6 +35,10 @@ class COFFAsmParser : public MCAsmParserExtension {
unsigned Characteristics,
SectionKind Kind);
+ bool ParseSectionSwitch(StringRef Section, unsigned Characteristics,
+ SectionKind Kind, StringRef COMDATSymName,
+ COFF::COMDATType Type, const MCSectionCOFF *Assoc);
+
bool ParseSectionName(StringRef &SectionName);
bool ParseSectionFlags(StringRef FlagsString, unsigned* Flags);
@@ -111,6 +115,8 @@ class COFFAsmParser : public MCAsmParserExtension {
bool ParseDirectiveType(StringRef, SMLoc);
bool ParseDirectiveEndef(StringRef, SMLoc);
bool ParseDirectiveSecRel32(StringRef, SMLoc);
+ bool parseCOMDATTypeAndAssoc(COFF::COMDATType &Type,
+ const MCSectionCOFF *&Assoc);
bool ParseDirectiveLinkOnce(StringRef, SMLoc);
// Win64 EH directives.
@@ -284,12 +290,22 @@ bool COFFAsmParser::ParseDirectiveSymbolAttribute(StringRef Directive, SMLoc) {
bool COFFAsmParser::ParseSectionSwitch(StringRef Section,
unsigned Characteristics,
SectionKind Kind) {
+ return ParseSectionSwitch(Section, Characteristics, Kind, "",
+ COFF::IMAGE_COMDAT_SELECT_ANY, 0);
+}
+
+bool COFFAsmParser::ParseSectionSwitch(StringRef Section,
+ unsigned Characteristics,
+ SectionKind Kind,
+ StringRef COMDATSymName,
+ COFF::COMDATType Type,
+ const MCSectionCOFF *Assoc) {
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in section switching directive");
Lex();
getStreamer().SwitchSection(getContext().getCOFFSection(
- Section, Characteristics, Kind));
+ Section, Characteristics, Kind, COMDATSymName, Type, Assoc));
return false;
}
@@ -303,7 +319,7 @@ bool COFFAsmParser::ParseSectionName(StringRef &SectionName) {
return false;
}
-// .section name [, "flags"]
+// .section name [, "flags"] [, identifier [ identifier ], identifier]
//
// Supported flags:
// a: Ignored.
@@ -340,11 +356,30 @@ bool COFFAsmParser::ParseDirectiveSection(StringRef, SMLoc) {
return true;
}
+ COFF::COMDATType Type = COFF::IMAGE_COMDAT_SELECT_ANY;
+ const MCSectionCOFF *Assoc = 0;
+ StringRef COMDATSymName;
+ if (getLexer().is(AsmToken::Comma)) {
+ Lex();
+
+ Flags |= COFF::IMAGE_SCN_LNK_COMDAT;
+
+ if (parseCOMDATTypeAndAssoc(Type, Assoc))
+ return true;
+
+ if (getLexer().isNot(AsmToken::Comma))
+ return TokError("expected comma in directive");
+ Lex();
+
+ if (getParser().parseIdentifier(COMDATSymName))
+ return TokError("expected identifier in directive");
+ }
+
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in directive");
SectionKind Kind = computeSectionKind(Flags);
- ParseSectionSwitch(SectionName, Flags, Kind);
+ ParseSectionSwitch(SectionName, Flags, Kind, COMDATSymName, Type, Assoc);
return false;
}
@@ -409,37 +444,29 @@ bool COFFAsmParser::ParseDirectiveSecRel32(StringRef, SMLoc) {
return false;
}
-/// ParseDirectiveLinkOnce
-/// ::= .linkonce [ identifier [ identifier ] ]
-bool COFFAsmParser::ParseDirectiveLinkOnce(StringRef, SMLoc Loc) {
- COFF::COMDATType Type = COFF::IMAGE_COMDAT_SELECT_ANY;
-
- if (getLexer().is(AsmToken::Identifier)) {
- StringRef TypeId = getTok().getIdentifier();
+/// ::= [ identifier [ identifier ] ]
+bool COFFAsmParser::parseCOMDATTypeAndAssoc(COFF::COMDATType &Type,
+ const MCSectionCOFF *&Assoc) {
+ StringRef TypeId = getTok().getIdentifier();
- Type = StringSwitch<COFF::COMDATType>(TypeId)
- .Case("one_only", COFF::IMAGE_COMDAT_SELECT_NODUPLICATES)
- .Case("discard", COFF::IMAGE_COMDAT_SELECT_ANY)
- .Case("same_size", COFF::IMAGE_COMDAT_SELECT_SAME_SIZE)
- .Case("same_contents", COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH)
- .Case("associative", COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE)
- .Case("largest", COFF::IMAGE_COMDAT_SELECT_LARGEST)
- .Case("newest", COFF::IMAGE_COMDAT_SELECT_NEWEST)
- .Default((COFF::COMDATType)0);
+ Type = StringSwitch<COFF::COMDATType>(TypeId)
+ .Case("one_only", COFF::IMAGE_COMDAT_SELECT_NODUPLICATES)
+ .Case("discard", COFF::IMAGE_COMDAT_SELECT_ANY)
+ .Case("same_size", COFF::IMAGE_COMDAT_SELECT_SAME_SIZE)
+ .Case("same_contents", COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH)
+ .Case("associative", COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE)
+ .Case("largest", COFF::IMAGE_COMDAT_SELECT_LARGEST)
+ .Case("newest", COFF::IMAGE_COMDAT_SELECT_NEWEST)
+ .Default((COFF::COMDATType)0);
- if (Type == 0)
- return TokError(Twine("unrecognized COMDAT type '" + TypeId + "'"));
+ if (Type == 0)
+ return TokError(Twine("unrecognized COMDAT type '" + TypeId + "'"));
- Lex();
- }
-
- const MCSectionCOFF *Current = static_cast<const MCSectionCOFF*>(
- getStreamer().getCurrentSection().first);
+ Lex();
- const MCSectionCOFF *Assoc = 0;
if (Type == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
- StringRef AssocName;
SMLoc Loc = getTok().getLoc();
+ StringRef AssocName;
if (ParseSectionName(AssocName))
return TokError("expected associated section name");
@@ -447,14 +474,33 @@ bool COFFAsmParser::ParseDirectiveLinkOnce(StringRef, SMLoc Loc) {
getContext().getCOFFSection(AssocName));
if (!Assoc)
return Error(Loc, "cannot associate unknown section '" + AssocName + "'");
- if (Assoc == Current)
- return Error(Loc, "cannot associate a section with itself");
if (!(Assoc->getCharacteristics() & COFF::IMAGE_SCN_LNK_COMDAT))
return Error(Loc, "associated section must be a COMDAT section");
if (Assoc->getSelection() == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE)
return Error(Loc, "associated section cannot be itself associative");
}
+ return false;
+}
+
+/// ParseDirectiveLinkOnce
+/// ::= .linkonce [ identifier [ identifier ] ]
+bool COFFAsmParser::ParseDirectiveLinkOnce(StringRef, SMLoc Loc) {
+ COFF::COMDATType Type = COFF::IMAGE_COMDAT_SELECT_ANY;
+ const MCSectionCOFF *Assoc = 0;
+ if (getLexer().is(AsmToken::Identifier))
+ if (parseCOMDATTypeAndAssoc(Type, Assoc))
+ return true;
+
+ const MCSectionCOFF *Current = static_cast<const MCSectionCOFF*>(
+ getStreamer().getCurrentSection().first);
+
+
+ if (Type == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
+ if (Assoc == Current)
+ return Error(Loc, "cannot associate a section with itself");
+ }
+
if (Current->getCharacteristics() & COFF::IMAGE_SCN_LNK_COMDAT)
return Error(Loc, Twine("section '") + Current->getSectionName() +
"' is already linkonce");
diff --git a/lib/MC/WinCOFFStreamer.cpp b/lib/MC/WinCOFFStreamer.cpp
index 6744df5..7fc114c 100644
--- a/lib/MC/WinCOFFStreamer.cpp
+++ b/lib/MC/WinCOFFStreamer.cpp
@@ -151,7 +151,8 @@ void WinCOFFStreamer::AddCommonSymbol(MCSymbol *Symbol, uint64_t Size,
int Selection = COFF::IMAGE_COMDAT_SELECT_LARGEST;
const MCSection *Section = MCStreamer::getContext().getCOFFSection(
- SectionName, Characteristics, SectionKind::getBSS(), Selection);
+ SectionName, Characteristics, SectionKind::getBSS(), Symbol->getName(),
+ Selection);
MCSectionData &SectionData = getAssembler().getOrCreateSectionData(*Section);
diff --git a/test/MC/COFF/section-comdat.s b/test/MC/COFF/section-comdat.s
new file mode 100644
index 0000000..dd5be87
--- /dev/null
+++ b/test/MC/COFF/section-comdat.s
@@ -0,0 +1,188 @@
+// RUN: llvm-mc -triple i386-pc-win32 -filetype=obj %s | llvm-readobj -s -t | FileCheck %s
+// RUN: llvm-mc -triple x86_64-pc-win32 -filetype=obj %s | llvm-readobj -s -t | FileCheck %s
+
+.section assocSec
+.linkonce
+.long 1
+
+.section secName, "dr", discard, "Symbol1"
+.globl Symbol1
+Symbol1:
+.long 1
+
+.section secName, "dr", one_only, "Symbol2"
+.globl Symbol2
+Symbol2:
+.long 1
+
+.section SecName, "dr", same_size, "Symbol3"
+.globl Symbol3
+Symbol3:
+.long 1
+
+.section SecName, "dr", same_contents, "Symbol4"
+.globl Symbol4
+Symbol4:
+.long 1
+
+.section SecName, "dr", associative assocSec, "Symbol5"
+.globl Symbol5
+Symbol5:
+.long 1
+
+.section SecName, "dr", largest, "Symbol6"
+.globl Symbol6
+Symbol6:
+.long 1
+
+.section SecName, "dr", newest, "Symbol7"
+.globl Symbol7
+Symbol7:
+.long 1
+
+// CHECK: Sections [
+// CHECK: Section {
+// CHECK: Number: 1
+// CHECK: Name: assocSec
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Number: 2
+// CHECK: Name: secName
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Number: 3
+// CHECK: Name: secName
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Number: 4
+// CHECK: Name: SecName
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Number: 5
+// CHECK: Name: SecName
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Number: 6
+// CHECK: Name: SecName
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Number: 7
+// CHECK: Name: SecName
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: Section {
+// CHECK: Number: 8
+// CHECK: Name: SecName
+// CHECK: Characteristics [
+// CHECK: IMAGE_SCN_LNK_COMDAT
+// CHECK: ]
+// CHECK: }
+// CHECK: ]
+// CHECK: Symbols [
+// CHECK: Symbol {
+// CHECK: Name: assocSec
+// CHECK: Section: assocSec (1)
+// CHECK: AuxSectionDef {
+// CHECK: Selection: Any
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: secName
+// CHECK: Section: secName (2)
+// CHECK: AuxSectionDef {
+// CHECK: Selection: Any
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: secName
+// CHECK: Section: secName (3)
+// CHECK: AuxSectionDef {
+// CHECK: Selection: NoDuplicates
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: SecName
+// CHECK: Section: SecName (4)
+// CHECK: AuxSectionDef {
+// CHECK: Selection: SameSize
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: SecName
+// CHECK: Section: SecName (5)
+// CHECK: AuxSymbolCount: 1
+// CHECK: AuxSectionDef {
+// CHECK: Selection: ExactMatch
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: SecName
+// CHECK: Section: SecName (6)
+// CHECK: AuxSectionDef {
+// CHECK: Selection: Associative
+// CHECK: AssocSection: assocSec (1)
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: SecName
+// CHECK: Section: SecName (7)
+// CHECK: AuxSectionDef {
+// CHECK: Selection: Largest
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: SecName
+// CHECK: Section: SecName (8)
+// CHECK: AuxSectionDef {
+// CHECK: Selection: Newest (0x7)
+// CHECK: }
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: Symbol1
+// CHECK: Section: secName (2)
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: Symbol2
+// CHECK: Section: secName (3)
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: Symbol3
+// CHECK: Section: SecName (4)
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: Symbol4
+// CHECK: Section: SecName (5)
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: Symbol5
+// CHECK: Section: SecName (6)
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: Symbol6
+// CHECK: Section: SecName (7)
+// CHECK: }
+// CHECK: Symbol {
+// CHECK: Name: Symbol7
+// CHECK: Section: SecName (8)
+// CHECK: }
+// CHECK: ]