1 //===- llvm/unittest/CodeGen/AsmPrinterDwarfTest.cpp ----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "TestAsmPrinter.h"
10 #include "llvm/CodeGen/AsmPrinter.h"
11 #include "llvm/CodeGen/MachineModuleInfo.h"
12 #include "llvm/IR/LegacyPassManager.h"
13 #include "llvm/IR/Module.h"
14 #include "llvm/IR/PassManager.h"
15 #include "llvm/MC/MCContext.h"
16 #include "llvm/MC/MCSectionELF.h"
17 #include "llvm/Target/TargetMachine.h"
18 #include "llvm/Testing/Support/Error.h"
19 
20 using namespace llvm;
21 using testing::_;
22 using testing::InSequence;
23 using testing::SaveArg;
24 
25 namespace {
26 
27 class AsmPrinterFixtureBase : public testing::Test {
setupTestPrinter(const std::string & TripleStr,unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat)28   void setupTestPrinter(const std::string &TripleStr, unsigned DwarfVersion,
29                         dwarf::DwarfFormat DwarfFormat) {
30     auto ExpectedTestPrinter =
31         TestAsmPrinter::create(TripleStr, DwarfVersion, DwarfFormat);
32     ASSERT_THAT_EXPECTED(ExpectedTestPrinter, Succeeded());
33     TestPrinter = std::move(ExpectedTestPrinter.get());
34   }
35 
36 protected:
init(const std::string & TripleStr,unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat)37   bool init(const std::string &TripleStr, unsigned DwarfVersion,
38             dwarf::DwarfFormat DwarfFormat) {
39     setupTestPrinter(TripleStr, DwarfVersion, DwarfFormat);
40     return TestPrinter != nullptr;
41   }
42 
43   std::unique_ptr<TestAsmPrinter> TestPrinter;
44 };
45 
46 class AsmPrinterEmitDwarfSymbolReferenceTest : public AsmPrinterFixtureBase {
47 protected:
init(const std::string & TripleStr,unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat)48   bool init(const std::string &TripleStr, unsigned DwarfVersion,
49             dwarf::DwarfFormat DwarfFormat) {
50     if (!AsmPrinterFixtureBase::init(TripleStr, DwarfVersion, DwarfFormat))
51       return false;
52 
53     // Create a symbol which will be emitted in the tests and associate it
54     // with a section because that is required in some code paths.
55 
56     Val = TestPrinter->getCtx().createTempSymbol();
57     Sec = TestPrinter->getCtx().getELFSection(".tst", ELF::SHT_PROGBITS, 0);
58     SecBeginSymbol = Sec->getBeginSymbol();
59     TestPrinter->getMS().SwitchSection(Sec);
60     TestPrinter->getMS().emitLabel(Val);
61     return true;
62   }
63 
64   MCSymbol *Val = nullptr;
65   MCSection *Sec = nullptr;
66   MCSymbol *SecBeginSymbol = nullptr;
67 };
68 
TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest,COFF)69 TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, COFF) {
70   if (!init("x86_64-pc-windows", /*DwarfVersion=*/4, dwarf::DWARF32))
71     return;
72 
73   EXPECT_CALL(TestPrinter->getMS(), EmitCOFFSecRel32(Val, 0));
74   TestPrinter->getAP()->emitDwarfSymbolReference(Val, false);
75 }
76 
TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest,COFFForceOffset)77 TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, COFFForceOffset) {
78   if (!init("x86_64-pc-windows", /*DwarfVersion=*/4, dwarf::DWARF32))
79     return;
80 
81   EXPECT_CALL(TestPrinter->getMS(),
82               emitAbsoluteSymbolDiff(Val, SecBeginSymbol, 4));
83   TestPrinter->getAP()->emitDwarfSymbolReference(Val, true);
84 }
85 
TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest,ELFDWARF32)86 TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, ELFDWARF32) {
87   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
88     return;
89 
90   const MCExpr *Arg0 = nullptr;
91   EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 4, _))
92       .WillOnce(SaveArg<0>(&Arg0));
93   TestPrinter->getAP()->emitDwarfSymbolReference(Val, false);
94 
95   const MCSymbolRefExpr *ActualArg0 = dyn_cast_or_null<MCSymbolRefExpr>(Arg0);
96   ASSERT_NE(ActualArg0, nullptr);
97   EXPECT_EQ(&(ActualArg0->getSymbol()), Val);
98 }
99 
TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest,ELFDWARF32ForceOffset)100 TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, ELFDWARF32ForceOffset) {
101   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
102     return;
103 
104   EXPECT_CALL(TestPrinter->getMS(),
105               emitAbsoluteSymbolDiff(Val, SecBeginSymbol, 4));
106   TestPrinter->getAP()->emitDwarfSymbolReference(Val, true);
107 }
108 
TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest,ELFDWARF64)109 TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, ELFDWARF64) {
110   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))
111     return;
112 
113   const MCExpr *Arg0 = nullptr;
114   EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 8, _))
115       .WillOnce(SaveArg<0>(&Arg0));
116   TestPrinter->getAP()->emitDwarfSymbolReference(Val, false);
117 
118   const MCSymbolRefExpr *ActualArg0 = dyn_cast_or_null<MCSymbolRefExpr>(Arg0);
119   ASSERT_NE(ActualArg0, nullptr);
120   EXPECT_EQ(&(ActualArg0->getSymbol()), Val);
121 }
122 
TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest,ELFDWARF64ForceOffset)123 TEST_F(AsmPrinterEmitDwarfSymbolReferenceTest, ELFDWARF64ForceOffset) {
124   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))
125     return;
126 
127   EXPECT_CALL(TestPrinter->getMS(),
128               emitAbsoluteSymbolDiff(Val, SecBeginSymbol, 8));
129   TestPrinter->getAP()->emitDwarfSymbolReference(Val, true);
130 }
131 
132 class AsmPrinterEmitDwarfStringOffsetTest : public AsmPrinterFixtureBase {
133 protected:
init(const std::string & TripleStr,unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat)134   bool init(const std::string &TripleStr, unsigned DwarfVersion,
135             dwarf::DwarfFormat DwarfFormat) {
136     if (!AsmPrinterFixtureBase::init(TripleStr, DwarfVersion, DwarfFormat))
137       return false;
138 
139     Val.Index = DwarfStringPoolEntry::NotIndexed;
140     Val.Symbol = TestPrinter->getCtx().createTempSymbol();
141     Val.Offset = 42;
142     return true;
143   }
144 
145   DwarfStringPoolEntry Val;
146 };
147 
TEST_F(AsmPrinterEmitDwarfStringOffsetTest,DWARF32)148 TEST_F(AsmPrinterEmitDwarfStringOffsetTest, DWARF32) {
149   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
150     return;
151 
152   const MCExpr *Arg0 = nullptr;
153   EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 4, _))
154       .WillOnce(SaveArg<0>(&Arg0));
155   TestPrinter->getAP()->emitDwarfStringOffset(Val);
156 
157   const MCSymbolRefExpr *ActualArg0 = dyn_cast_or_null<MCSymbolRefExpr>(Arg0);
158   ASSERT_NE(ActualArg0, nullptr);
159   EXPECT_EQ(&(ActualArg0->getSymbol()), Val.Symbol);
160 }
161 
TEST_F(AsmPrinterEmitDwarfStringOffsetTest,DWARF32NoRelocationsAcrossSections)162 TEST_F(AsmPrinterEmitDwarfStringOffsetTest,
163        DWARF32NoRelocationsAcrossSections) {
164   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
165     return;
166 
167   TestPrinter->setDwarfUsesRelocationsAcrossSections(false);
168   EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val.Offset, 4));
169   TestPrinter->getAP()->emitDwarfStringOffset(Val);
170 }
171 
TEST_F(AsmPrinterEmitDwarfStringOffsetTest,DWARF64)172 TEST_F(AsmPrinterEmitDwarfStringOffsetTest, DWARF64) {
173   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))
174     return;
175 
176   const MCExpr *Arg0 = nullptr;
177   EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 8, _))
178       .WillOnce(SaveArg<0>(&Arg0));
179   TestPrinter->getAP()->emitDwarfStringOffset(Val);
180 
181   const MCSymbolRefExpr *ActualArg0 = dyn_cast_or_null<MCSymbolRefExpr>(Arg0);
182   ASSERT_NE(ActualArg0, nullptr);
183   EXPECT_EQ(&(ActualArg0->getSymbol()), Val.Symbol);
184 }
185 
TEST_F(AsmPrinterEmitDwarfStringOffsetTest,DWARF64NoRelocationsAcrossSections)186 TEST_F(AsmPrinterEmitDwarfStringOffsetTest,
187        DWARF64NoRelocationsAcrossSections) {
188   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))
189     return;
190 
191   TestPrinter->setDwarfUsesRelocationsAcrossSections(false);
192   EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val.Offset, 8));
193   TestPrinter->getAP()->emitDwarfStringOffset(Val);
194 }
195 
196 class AsmPrinterEmitDwarfOffsetTest : public AsmPrinterFixtureBase {
197 protected:
init(const std::string & TripleStr,unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat)198   bool init(const std::string &TripleStr, unsigned DwarfVersion,
199             dwarf::DwarfFormat DwarfFormat) {
200     if (!AsmPrinterFixtureBase::init(TripleStr, DwarfVersion, DwarfFormat))
201       return false;
202 
203     Label = TestPrinter->getCtx().createTempSymbol();
204     return true;
205   }
206 
207   MCSymbol *Label = nullptr;
208   uint64_t Offset = 42;
209 };
210 
TEST_F(AsmPrinterEmitDwarfOffsetTest,DWARF32)211 TEST_F(AsmPrinterEmitDwarfOffsetTest, DWARF32) {
212   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
213     return;
214 
215   const MCExpr *Arg0 = nullptr;
216   EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 4, _))
217       .WillOnce(SaveArg<0>(&Arg0));
218   TestPrinter->getAP()->emitDwarfOffset(Label, Offset);
219 
220   const MCBinaryExpr *ActualArg0 = dyn_cast_or_null<MCBinaryExpr>(Arg0);
221   ASSERT_NE(ActualArg0, nullptr);
222   EXPECT_EQ(ActualArg0->getOpcode(), MCBinaryExpr::Add);
223 
224   const MCSymbolRefExpr *ActualLHS =
225       dyn_cast_or_null<MCSymbolRefExpr>(ActualArg0->getLHS());
226   ASSERT_NE(ActualLHS, nullptr);
227   EXPECT_EQ(&(ActualLHS->getSymbol()), Label);
228 
229   const MCConstantExpr *ActualRHS =
230       dyn_cast_or_null<MCConstantExpr>(ActualArg0->getRHS());
231   ASSERT_NE(ActualRHS, nullptr);
232   EXPECT_EQ(static_cast<uint64_t>(ActualRHS->getValue()), Offset);
233 }
234 
TEST_F(AsmPrinterEmitDwarfOffsetTest,DWARF64)235 TEST_F(AsmPrinterEmitDwarfOffsetTest, DWARF64) {
236   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))
237     return;
238 
239   const MCExpr *Arg0 = nullptr;
240   EXPECT_CALL(TestPrinter->getMS(), emitValueImpl(_, 8, _))
241       .WillOnce(SaveArg<0>(&Arg0));
242   TestPrinter->getAP()->emitDwarfOffset(Label, Offset);
243 
244   const MCBinaryExpr *ActualArg0 = dyn_cast_or_null<MCBinaryExpr>(Arg0);
245   ASSERT_NE(ActualArg0, nullptr);
246   EXPECT_EQ(ActualArg0->getOpcode(), MCBinaryExpr::Add);
247 
248   const MCSymbolRefExpr *ActualLHS =
249       dyn_cast_or_null<MCSymbolRefExpr>(ActualArg0->getLHS());
250   ASSERT_NE(ActualLHS, nullptr);
251   EXPECT_EQ(&(ActualLHS->getSymbol()), Label);
252 
253   const MCConstantExpr *ActualRHS =
254       dyn_cast_or_null<MCConstantExpr>(ActualArg0->getRHS());
255   ASSERT_NE(ActualRHS, nullptr);
256   EXPECT_EQ(static_cast<uint64_t>(ActualRHS->getValue()), Offset);
257 }
258 
259 class AsmPrinterEmitDwarfLengthOrOffsetTest : public AsmPrinterFixtureBase {
260 protected:
261   uint64_t Val = 42;
262 };
263 
TEST_F(AsmPrinterEmitDwarfLengthOrOffsetTest,DWARF32)264 TEST_F(AsmPrinterEmitDwarfLengthOrOffsetTest, DWARF32) {
265   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
266     return;
267 
268   EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val, 4));
269   TestPrinter->getAP()->emitDwarfLengthOrOffset(Val);
270 }
271 
TEST_F(AsmPrinterEmitDwarfLengthOrOffsetTest,DWARF64)272 TEST_F(AsmPrinterEmitDwarfLengthOrOffsetTest, DWARF64) {
273   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))
274     return;
275 
276   EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val, 8));
277   TestPrinter->getAP()->emitDwarfLengthOrOffset(Val);
278 }
279 
280 class AsmPrinterGetUnitLengthFieldByteSizeTest : public AsmPrinterFixtureBase {
281 };
282 
TEST_F(AsmPrinterGetUnitLengthFieldByteSizeTest,DWARF32)283 TEST_F(AsmPrinterGetUnitLengthFieldByteSizeTest, DWARF32) {
284   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
285     return;
286 
287   EXPECT_EQ(TestPrinter->getAP()->getUnitLengthFieldByteSize(), 4u);
288 }
289 
TEST_F(AsmPrinterGetUnitLengthFieldByteSizeTest,DWARF64)290 TEST_F(AsmPrinterGetUnitLengthFieldByteSizeTest, DWARF64) {
291   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))
292     return;
293 
294   EXPECT_EQ(TestPrinter->getAP()->getUnitLengthFieldByteSize(), 12u);
295 }
296 
297 class AsmPrinterMaybeEmitDwarf64MarkTest : public AsmPrinterFixtureBase {};
298 
TEST_F(AsmPrinterMaybeEmitDwarf64MarkTest,DWARF32)299 TEST_F(AsmPrinterMaybeEmitDwarf64MarkTest, DWARF32) {
300   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
301     return;
302 
303   EXPECT_CALL(TestPrinter->getMS(), emitIntValue(_, _)).Times(0);
304   TestPrinter->getAP()->maybeEmitDwarf64Mark();
305 }
306 
TEST_F(AsmPrinterMaybeEmitDwarf64MarkTest,DWARF64)307 TEST_F(AsmPrinterMaybeEmitDwarf64MarkTest, DWARF64) {
308   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))
309     return;
310 
311   EXPECT_CALL(TestPrinter->getMS(), emitIntValue(dwarf::DW_LENGTH_DWARF64, 4));
312   TestPrinter->getAP()->maybeEmitDwarf64Mark();
313 }
314 
315 class AsmPrinterEmitDwarfUnitLengthAsIntTest : public AsmPrinterFixtureBase {
316 protected:
317   uint64_t Val = 42;
318 };
319 
TEST_F(AsmPrinterEmitDwarfUnitLengthAsIntTest,DWARF32)320 TEST_F(AsmPrinterEmitDwarfUnitLengthAsIntTest, DWARF32) {
321   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
322     return;
323 
324   EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val, 4));
325   TestPrinter->getAP()->emitDwarfUnitLength(Val, "");
326 }
327 
TEST_F(AsmPrinterEmitDwarfUnitLengthAsIntTest,DWARF64)328 TEST_F(AsmPrinterEmitDwarfUnitLengthAsIntTest, DWARF64) {
329   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))
330     return;
331 
332   InSequence S;
333   EXPECT_CALL(TestPrinter->getMS(), emitIntValue(dwarf::DW_LENGTH_DWARF64, 4));
334   EXPECT_CALL(TestPrinter->getMS(), emitIntValue(Val, 8));
335 
336   TestPrinter->getAP()->emitDwarfUnitLength(Val, "");
337 }
338 
339 class AsmPrinterEmitDwarfUnitLengthAsHiLoDiffTest
340     : public AsmPrinterFixtureBase {
341 protected:
init(const std::string & TripleStr,unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat)342   bool init(const std::string &TripleStr, unsigned DwarfVersion,
343             dwarf::DwarfFormat DwarfFormat) {
344     if (!AsmPrinterFixtureBase::init(TripleStr, DwarfVersion, DwarfFormat))
345       return false;
346 
347     Hi = TestPrinter->getCtx().createTempSymbol();
348     Lo = TestPrinter->getCtx().createTempSymbol();
349     return true;
350   }
351 
352   MCSymbol *Hi = nullptr;
353   MCSymbol *Lo = nullptr;
354 };
355 
TEST_F(AsmPrinterEmitDwarfUnitLengthAsHiLoDiffTest,DWARF32)356 TEST_F(AsmPrinterEmitDwarfUnitLengthAsHiLoDiffTest, DWARF32) {
357   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
358     return;
359 
360   EXPECT_CALL(TestPrinter->getMS(), emitAbsoluteSymbolDiff(Hi, Lo, 4));
361   TestPrinter->getAP()->emitDwarfUnitLength(Hi, Lo, "");
362 }
363 
TEST_F(AsmPrinterEmitDwarfUnitLengthAsHiLoDiffTest,DWARF64)364 TEST_F(AsmPrinterEmitDwarfUnitLengthAsHiLoDiffTest, DWARF64) {
365   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF64))
366     return;
367 
368   InSequence S;
369   EXPECT_CALL(TestPrinter->getMS(), emitIntValue(dwarf::DW_LENGTH_DWARF64, 4));
370   EXPECT_CALL(TestPrinter->getMS(), emitAbsoluteSymbolDiff(Hi, Lo, 8));
371 
372   TestPrinter->getAP()->emitDwarfUnitLength(Hi, Lo, "");
373 }
374 
375 class AsmPrinterHandlerTest : public AsmPrinterFixtureBase {
376   class TestHandler : public AsmPrinterHandler {
377     AsmPrinterHandlerTest &Test;
378 
379   public:
TestHandler(AsmPrinterHandlerTest & Test)380     TestHandler(AsmPrinterHandlerTest &Test) : Test(Test) {}
~TestHandler()381     virtual ~TestHandler() {}
setSymbolSize(const MCSymbol * Sym,uint64_t Size)382     virtual void setSymbolSize(const MCSymbol *Sym, uint64_t Size) override {}
beginModule(Module * M)383     virtual void beginModule(Module *M) override { Test.BeginCount++; }
endModule()384     virtual void endModule() override { Test.EndCount++; }
beginFunction(const MachineFunction * MF)385     virtual void beginFunction(const MachineFunction *MF) override {}
endFunction(const MachineFunction * MF)386     virtual void endFunction(const MachineFunction *MF) override {}
beginInstruction(const MachineInstr * MI)387     virtual void beginInstruction(const MachineInstr *MI) override {}
endInstruction()388     virtual void endInstruction() override {}
389   };
390 
391 protected:
init(const std::string & TripleStr,unsigned DwarfVersion,dwarf::DwarfFormat DwarfFormat)392   bool init(const std::string &TripleStr, unsigned DwarfVersion,
393             dwarf::DwarfFormat DwarfFormat) {
394     if (!AsmPrinterFixtureBase::init(TripleStr, DwarfVersion, DwarfFormat))
395       return false;
396 
397     auto *AP = TestPrinter->getAP();
398     AP->addAsmPrinterHandler(AsmPrinter::HandlerInfo(
399         std::unique_ptr<AsmPrinterHandler>(new TestHandler(*this)),
400         "TestTimerName", "TestTimerDesc", "TestGroupName", "TestGroupDesc"));
401     LLVMTargetMachine *LLVMTM = static_cast<LLVMTargetMachine *>(&AP->TM);
402     legacy::PassManager PM;
403     PM.add(new MachineModuleInfoWrapperPass(LLVMTM));
404     PM.add(TestPrinter->releaseAP()); // Takes ownership of destroying AP
405     LLVMContext Context;
406     std::unique_ptr<Module> M(new Module("TestModule", Context));
407     M->setDataLayout(LLVMTM->createDataLayout());
408     PM.run(*M);
409     // Now check that we can run it twice.
410     AP->addAsmPrinterHandler(AsmPrinter::HandlerInfo(
411         std::unique_ptr<AsmPrinterHandler>(new TestHandler(*this)),
412         "TestTimerName", "TestTimerDesc", "TestGroupName", "TestGroupDesc"));
413     PM.run(*M);
414     return true;
415   }
416 
417   int BeginCount = 0;
418   int EndCount = 0;
419 };
420 
TEST_F(AsmPrinterHandlerTest,Basic)421 TEST_F(AsmPrinterHandlerTest, Basic) {
422   if (!init("x86_64-pc-linux", /*DwarfVersion=*/4, dwarf::DWARF32))
423     return;
424 
425   ASSERT_EQ(BeginCount, 3);
426   ASSERT_EQ(EndCount, 3);
427 }
428 
429 } // end namespace
430