xref: /llvm-project/llvm/unittests/SandboxIR/RegionTest.cpp (revision 4b209c5d87c8b8eb4bbf2750ea9daa5927a13699)
19efc761dSJorge Gorbe Moya //===- RegionTest.cpp -----------------------------------------------------===//
29efc761dSJorge Gorbe Moya //
39efc761dSJorge Gorbe Moya // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49efc761dSJorge Gorbe Moya // See https://llvm.org/LICENSE.txt for license information.
59efc761dSJorge Gorbe Moya // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69efc761dSJorge Gorbe Moya //
79efc761dSJorge Gorbe Moya //===----------------------------------------------------------------------===//
89efc761dSJorge Gorbe Moya 
99efc761dSJorge Gorbe Moya #include "llvm/SandboxIR/Region.h"
10*4b209c5dSvporpo #include "llvm/Analysis/TargetTransformInfo.h"
119efc761dSJorge Gorbe Moya #include "llvm/AsmParser/Parser.h"
122018f4ccSVasileios Porpodas #include "llvm/SandboxIR/Context.h"
13e22b07e7Svporpo #include "llvm/SandboxIR/Function.h"
142018f4ccSVasileios Porpodas #include "llvm/SandboxIR/Instruction.h"
159efc761dSJorge Gorbe Moya #include "llvm/Support/SourceMgr.h"
169efc761dSJorge Gorbe Moya #include "gmock/gmock-matchers.h"
179efc761dSJorge Gorbe Moya #include "gtest/gtest.h"
189efc761dSJorge Gorbe Moya 
199efc761dSJorge Gorbe Moya using namespace llvm;
209efc761dSJorge Gorbe Moya 
219efc761dSJorge Gorbe Moya struct RegionTest : public testing::Test {
229efc761dSJorge Gorbe Moya   LLVMContext C;
239efc761dSJorge Gorbe Moya   std::unique_ptr<Module> M;
24*4b209c5dSvporpo   std::unique_ptr<TargetTransformInfo> TTI;
259efc761dSJorge Gorbe Moya 
269efc761dSJorge Gorbe Moya   void parseIR(LLVMContext &C, const char *IR) {
279efc761dSJorge Gorbe Moya     SMDiagnostic Err;
289efc761dSJorge Gorbe Moya     M = parseAssemblyString(IR, Err, C);
29*4b209c5dSvporpo     TTI = std::make_unique<TargetTransformInfo>(M->getDataLayout());
309efc761dSJorge Gorbe Moya     if (!M)
319efc761dSJorge Gorbe Moya       Err.print("RegionTest", errs());
329efc761dSJorge Gorbe Moya   }
339efc761dSJorge Gorbe Moya };
349efc761dSJorge Gorbe Moya 
359efc761dSJorge Gorbe Moya TEST_F(RegionTest, Basic) {
369efc761dSJorge Gorbe Moya   parseIR(C, R"IR(
379efc761dSJorge Gorbe Moya define i8 @foo(i8 %v0, i8 %v1) {
389efc761dSJorge Gorbe Moya   %t0 = add i8 %v0, 1
399efc761dSJorge Gorbe Moya   %t1 = add i8 %t0, %v1
409efc761dSJorge Gorbe Moya   ret i8 %t1
419efc761dSJorge Gorbe Moya }
429efc761dSJorge Gorbe Moya )IR");
439efc761dSJorge Gorbe Moya   llvm::Function *LLVMF = &*M->getFunction("foo");
449efc761dSJorge Gorbe Moya   sandboxir::Context Ctx(C);
459efc761dSJorge Gorbe Moya   auto *F = Ctx.createFunction(LLVMF);
469efc761dSJorge Gorbe Moya   auto *BB = &*F->begin();
479efc761dSJorge Gorbe Moya   auto It = BB->begin();
489efc761dSJorge Gorbe Moya   auto *T0 = cast<sandboxir::Instruction>(&*It++);
499efc761dSJorge Gorbe Moya   auto *T1 = cast<sandboxir::Instruction>(&*It++);
509efc761dSJorge Gorbe Moya   auto *Ret = cast<sandboxir::Instruction>(&*It++);
51*4b209c5dSvporpo   sandboxir::Region Rgn(Ctx, *TTI);
529efc761dSJorge Gorbe Moya 
539efc761dSJorge Gorbe Moya   // Check getContext.
549efc761dSJorge Gorbe Moya   EXPECT_EQ(&Ctx, &Rgn.getContext());
559efc761dSJorge Gorbe Moya 
569efc761dSJorge Gorbe Moya   // Check add / remove / empty.
579efc761dSJorge Gorbe Moya   EXPECT_TRUE(Rgn.empty());
589efc761dSJorge Gorbe Moya   Rgn.add(T0);
599efc761dSJorge Gorbe Moya   EXPECT_FALSE(Rgn.empty());
609efc761dSJorge Gorbe Moya   Rgn.remove(T0);
619efc761dSJorge Gorbe Moya   EXPECT_TRUE(Rgn.empty());
629efc761dSJorge Gorbe Moya 
639efc761dSJorge Gorbe Moya   // Check iteration.
649efc761dSJorge Gorbe Moya   Rgn.add(T0);
659efc761dSJorge Gorbe Moya   Rgn.add(T1);
669efc761dSJorge Gorbe Moya   Rgn.add(Ret);
679efc761dSJorge Gorbe Moya   // Use an ordered matcher because we're supposed to preserve the insertion
689efc761dSJorge Gorbe Moya   // order for determinism.
699efc761dSJorge Gorbe Moya   EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, T1, Ret));
709efc761dSJorge Gorbe Moya 
719efc761dSJorge Gorbe Moya   // Check contains
729efc761dSJorge Gorbe Moya   EXPECT_TRUE(Rgn.contains(T0));
739efc761dSJorge Gorbe Moya   Rgn.remove(T0);
749efc761dSJorge Gorbe Moya   EXPECT_FALSE(Rgn.contains(T0));
759efc761dSJorge Gorbe Moya 
769efc761dSJorge Gorbe Moya #ifndef NDEBUG
779efc761dSJorge Gorbe Moya   // Check equality comparison. Insert in reverse order into `Other` to check
789efc761dSJorge Gorbe Moya   // that comparison is order-independent.
79*4b209c5dSvporpo   sandboxir::Region Other(Ctx, *TTI);
809efc761dSJorge Gorbe Moya   Other.add(Ret);
819efc761dSJorge Gorbe Moya   EXPECT_NE(Rgn, Other);
829efc761dSJorge Gorbe Moya   Other.add(T1);
839efc761dSJorge Gorbe Moya   EXPECT_EQ(Rgn, Other);
849efc761dSJorge Gorbe Moya #endif
859efc761dSJorge Gorbe Moya }
869efc761dSJorge Gorbe Moya 
87b22c3c1eSJorge Gorbe Moya TEST_F(RegionTest, CallbackUpdates) {
88b22c3c1eSJorge Gorbe Moya   parseIR(C, R"IR(
89b22c3c1eSJorge Gorbe Moya define i8 @foo(i8 %v0, i8 %v1, ptr %ptr) {
90b22c3c1eSJorge Gorbe Moya   %t0 = add i8 %v0, 1
91b22c3c1eSJorge Gorbe Moya   %t1 = add i8 %t0, %v1
92b22c3c1eSJorge Gorbe Moya   ret i8 %t0
93b22c3c1eSJorge Gorbe Moya }
94b22c3c1eSJorge Gorbe Moya )IR");
95b22c3c1eSJorge Gorbe Moya   llvm::Function *LLVMF = &*M->getFunction("foo");
96b22c3c1eSJorge Gorbe Moya   sandboxir::Context Ctx(C);
97b22c3c1eSJorge Gorbe Moya   auto *F = Ctx.createFunction(LLVMF);
98b22c3c1eSJorge Gorbe Moya   auto *Ptr = F->getArg(2);
99b22c3c1eSJorge Gorbe Moya   auto *BB = &*F->begin();
100b22c3c1eSJorge Gorbe Moya   auto It = BB->begin();
101b22c3c1eSJorge Gorbe Moya   auto *T0 = cast<sandboxir::Instruction>(&*It++);
102b22c3c1eSJorge Gorbe Moya   auto *T1 = cast<sandboxir::Instruction>(&*It++);
103b22c3c1eSJorge Gorbe Moya   auto *Ret = cast<sandboxir::Instruction>(&*It++);
104*4b209c5dSvporpo   sandboxir::Region Rgn(Ctx, *TTI);
105b22c3c1eSJorge Gorbe Moya   Rgn.add(T0);
106b22c3c1eSJorge Gorbe Moya   Rgn.add(T1);
107b22c3c1eSJorge Gorbe Moya 
108b22c3c1eSJorge Gorbe Moya   // Test creation.
109b22c3c1eSJorge Gorbe Moya   auto *NewI = sandboxir::StoreInst::create(T0, Ptr, /*Align=*/std::nullopt,
110b22c3c1eSJorge Gorbe Moya                                             Ret->getIterator(), Ctx);
111b22c3c1eSJorge Gorbe Moya   EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, T1, NewI));
112b22c3c1eSJorge Gorbe Moya 
113b22c3c1eSJorge Gorbe Moya   // Test deletion.
114b22c3c1eSJorge Gorbe Moya   T1->eraseFromParent();
115b22c3c1eSJorge Gorbe Moya   EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, NewI));
116b22c3c1eSJorge Gorbe Moya }
117b22c3c1eSJorge Gorbe Moya 
1189efc761dSJorge Gorbe Moya TEST_F(RegionTest, MetadataFromIR) {
1199efc761dSJorge Gorbe Moya   parseIR(C, R"IR(
1209efc761dSJorge Gorbe Moya define i8 @foo(i8 %v0, i8 %v1) {
1219efc761dSJorge Gorbe Moya   %t0 = add i8 %v0, 1, !sandboxvec !0
1229efc761dSJorge Gorbe Moya   %t1 = add i8 %t0, %v1, !sandboxvec !1
1239efc761dSJorge Gorbe Moya   %t2 = add i8 %t1, %v1, !sandboxvec !1
1249efc761dSJorge Gorbe Moya   ret i8 %t2
1259efc761dSJorge Gorbe Moya }
1269efc761dSJorge Gorbe Moya 
1279efc761dSJorge Gorbe Moya !0 = distinct !{!"sandboxregion"}
1289efc761dSJorge Gorbe Moya !1 = distinct !{!"sandboxregion"}
1299efc761dSJorge Gorbe Moya )IR");
1309efc761dSJorge Gorbe Moya   llvm::Function *LLVMF = &*M->getFunction("foo");
1319efc761dSJorge Gorbe Moya   sandboxir::Context Ctx(C);
1329efc761dSJorge Gorbe Moya   auto *F = Ctx.createFunction(LLVMF);
1339efc761dSJorge Gorbe Moya   auto *BB = &*F->begin();
1349efc761dSJorge Gorbe Moya   auto It = BB->begin();
1359efc761dSJorge Gorbe Moya   auto *T0 = cast<sandboxir::Instruction>(&*It++);
1369efc761dSJorge Gorbe Moya   auto *T1 = cast<sandboxir::Instruction>(&*It++);
1379efc761dSJorge Gorbe Moya   auto *T2 = cast<sandboxir::Instruction>(&*It++);
1389efc761dSJorge Gorbe Moya 
1399efc761dSJorge Gorbe Moya   SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
140*4b209c5dSvporpo       sandboxir::Region::createRegionsFromMD(*F, *TTI);
1419efc761dSJorge Gorbe Moya   EXPECT_THAT(Regions[0]->insts(), testing::UnorderedElementsAre(T0));
1429efc761dSJorge Gorbe Moya   EXPECT_THAT(Regions[1]->insts(), testing::UnorderedElementsAre(T1, T2));
1439efc761dSJorge Gorbe Moya }
1449efc761dSJorge Gorbe Moya 
1452aa1dbf9SJorge Gorbe Moya TEST_F(RegionTest, NonContiguousRegion) {
1462aa1dbf9SJorge Gorbe Moya   parseIR(C, R"IR(
1472aa1dbf9SJorge Gorbe Moya define i8 @foo(i8 %v0, i8 %v1) {
1482aa1dbf9SJorge Gorbe Moya   %t0 = add i8 %v0, 1, !sandboxvec !0
1492aa1dbf9SJorge Gorbe Moya   %t1 = add i8 %t0, %v1
1502aa1dbf9SJorge Gorbe Moya   %t2 = add i8 %t1, %v1, !sandboxvec !0
1512aa1dbf9SJorge Gorbe Moya   ret i8 %t2
1522aa1dbf9SJorge Gorbe Moya }
1532aa1dbf9SJorge Gorbe Moya 
1542aa1dbf9SJorge Gorbe Moya !0 = distinct !{!"sandboxregion"}
1552aa1dbf9SJorge Gorbe Moya )IR");
1562aa1dbf9SJorge Gorbe Moya   llvm::Function *LLVMF = &*M->getFunction("foo");
1572aa1dbf9SJorge Gorbe Moya   sandboxir::Context Ctx(C);
1582aa1dbf9SJorge Gorbe Moya   auto *F = Ctx.createFunction(LLVMF);
1592aa1dbf9SJorge Gorbe Moya   auto *BB = &*F->begin();
1602aa1dbf9SJorge Gorbe Moya   auto It = BB->begin();
1612aa1dbf9SJorge Gorbe Moya   auto *T0 = cast<sandboxir::Instruction>(&*It++);
1622aa1dbf9SJorge Gorbe Moya   [[maybe_unused]] auto *T1 = cast<sandboxir::Instruction>(&*It++);
1632aa1dbf9SJorge Gorbe Moya   auto *T2 = cast<sandboxir::Instruction>(&*It++);
1642aa1dbf9SJorge Gorbe Moya 
1652aa1dbf9SJorge Gorbe Moya   SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
166*4b209c5dSvporpo       sandboxir::Region::createRegionsFromMD(*F, *TTI);
1672aa1dbf9SJorge Gorbe Moya   EXPECT_THAT(Regions[0]->insts(), testing::UnorderedElementsAre(T0, T2));
1682aa1dbf9SJorge Gorbe Moya }
1692aa1dbf9SJorge Gorbe Moya 
1709efc761dSJorge Gorbe Moya TEST_F(RegionTest, DumpedMetadata) {
1719efc761dSJorge Gorbe Moya   parseIR(C, R"IR(
1729efc761dSJorge Gorbe Moya define i8 @foo(i8 %v0, i8 %v1) {
1739efc761dSJorge Gorbe Moya   %t0 = add i8 %v0, 1
1749efc761dSJorge Gorbe Moya   %t1 = add i8 %t0, %v1
1759efc761dSJorge Gorbe Moya   %t2 = add i8 %t1, %v1
1769efc761dSJorge Gorbe Moya   ret i8 %t1
1779efc761dSJorge Gorbe Moya }
1789efc761dSJorge Gorbe Moya )IR");
1799efc761dSJorge Gorbe Moya   llvm::Function *LLVMF = &*M->getFunction("foo");
1809efc761dSJorge Gorbe Moya   sandboxir::Context Ctx(C);
1819efc761dSJorge Gorbe Moya   auto *F = Ctx.createFunction(LLVMF);
1829efc761dSJorge Gorbe Moya   auto *BB = &*F->begin();
1839efc761dSJorge Gorbe Moya   auto It = BB->begin();
1849efc761dSJorge Gorbe Moya   auto *T0 = cast<sandboxir::Instruction>(&*It++);
1859efc761dSJorge Gorbe Moya   [[maybe_unused]] auto *T1 = cast<sandboxir::Instruction>(&*It++);
1869efc761dSJorge Gorbe Moya   auto *T2 = cast<sandboxir::Instruction>(&*It++);
1879efc761dSJorge Gorbe Moya   [[maybe_unused]] auto *Ret = cast<sandboxir::Instruction>(&*It++);
188*4b209c5dSvporpo   sandboxir::Region Rgn(Ctx, *TTI);
1899efc761dSJorge Gorbe Moya   Rgn.add(T0);
190*4b209c5dSvporpo   sandboxir::Region Rgn2(Ctx, *TTI);
1919efc761dSJorge Gorbe Moya   Rgn2.add(T2);
1929efc761dSJorge Gorbe Moya 
1939efc761dSJorge Gorbe Moya   std::string output;
1949efc761dSJorge Gorbe Moya   llvm::raw_string_ostream RSO(output);
1959efc761dSJorge Gorbe Moya   M->print(RSO, nullptr, /*ShouldPreserveUseListOrder=*/true,
1969efc761dSJorge Gorbe Moya            /*IsForDebug=*/true);
1979efc761dSJorge Gorbe Moya 
1989efc761dSJorge Gorbe Moya   // TODO: Replace this with a lit test, which is more suitable for this kind
1999efc761dSJorge Gorbe Moya   // of IR comparison.
2009efc761dSJorge Gorbe Moya   std::string expected = R"(; ModuleID = '<string>'
2019efc761dSJorge Gorbe Moya source_filename = "<string>"
2029efc761dSJorge Gorbe Moya 
2039efc761dSJorge Gorbe Moya define i8 @foo(i8 %v0, i8 %v1) {
2049efc761dSJorge Gorbe Moya   %t0 = add i8 %v0, 1, !sandboxvec !0
2059efc761dSJorge Gorbe Moya   %t1 = add i8 %t0, %v1
2069efc761dSJorge Gorbe Moya   %t2 = add i8 %t1, %v1, !sandboxvec !1
2079efc761dSJorge Gorbe Moya   ret i8 %t1
2089efc761dSJorge Gorbe Moya }
2099efc761dSJorge Gorbe Moya 
2109efc761dSJorge Gorbe Moya !0 = distinct !{!"sandboxregion"}
2119efc761dSJorge Gorbe Moya !1 = distinct !{!"sandboxregion"}
2129efc761dSJorge Gorbe Moya )";
2139efc761dSJorge Gorbe Moya   EXPECT_EQ(expected, output);
2149efc761dSJorge Gorbe Moya }
2159efc761dSJorge Gorbe Moya 
2169efc761dSJorge Gorbe Moya TEST_F(RegionTest, MetadataRoundTrip) {
2179efc761dSJorge Gorbe Moya   parseIR(C, R"IR(
2189efc761dSJorge Gorbe Moya define i8 @foo(i8 %v0, i8 %v1) {
2199efc761dSJorge Gorbe Moya   %t0 = add i8 %v0, 1
2209efc761dSJorge Gorbe Moya   %t1 = add i8 %t0, %v1
2219efc761dSJorge Gorbe Moya   ret i8 %t1
2229efc761dSJorge Gorbe Moya }
2239efc761dSJorge Gorbe Moya )IR");
2249efc761dSJorge Gorbe Moya   llvm::Function *LLVMF = &*M->getFunction("foo");
2259efc761dSJorge Gorbe Moya   sandboxir::Context Ctx(C);
2269efc761dSJorge Gorbe Moya   auto *F = Ctx.createFunction(LLVMF);
2279efc761dSJorge Gorbe Moya   auto *BB = &*F->begin();
2289efc761dSJorge Gorbe Moya   auto It = BB->begin();
2299efc761dSJorge Gorbe Moya   auto *T0 = cast<sandboxir::Instruction>(&*It++);
2309efc761dSJorge Gorbe Moya   auto *T1 = cast<sandboxir::Instruction>(&*It++);
2319efc761dSJorge Gorbe Moya 
232*4b209c5dSvporpo   sandboxir::Region Rgn(Ctx, *TTI);
2339efc761dSJorge Gorbe Moya   Rgn.add(T0);
2349efc761dSJorge Gorbe Moya   Rgn.add(T1);
2359efc761dSJorge Gorbe Moya 
2369efc761dSJorge Gorbe Moya   SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
237*4b209c5dSvporpo       sandboxir::Region::createRegionsFromMD(*F, *TTI);
2389efc761dSJorge Gorbe Moya   ASSERT_EQ(1U, Regions.size());
2399efc761dSJorge Gorbe Moya #ifndef NDEBUG
2409efc761dSJorge Gorbe Moya   EXPECT_EQ(Rgn, *Regions[0].get());
2419efc761dSJorge Gorbe Moya #endif
2429efc761dSJorge Gorbe Moya }
243*4b209c5dSvporpo 
244*4b209c5dSvporpo TEST_F(RegionTest, RegionCost) {
245*4b209c5dSvporpo   parseIR(C, R"IR(
246*4b209c5dSvporpo define void @foo(i8 %v0, i8 %v1, i8 %v2) {
247*4b209c5dSvporpo   %add0 = add i8 %v0, 1
248*4b209c5dSvporpo   %add1 = add i8 %v1, 2
249*4b209c5dSvporpo   %add2 = add i8 %v2, 3
250*4b209c5dSvporpo   ret void
251*4b209c5dSvporpo }
252*4b209c5dSvporpo )IR");
253*4b209c5dSvporpo   llvm::Function *LLVMF = &*M->getFunction("foo");
254*4b209c5dSvporpo   auto *LLVMBB = &*LLVMF->begin();
255*4b209c5dSvporpo   auto LLVMIt = LLVMBB->begin();
256*4b209c5dSvporpo   auto *LLVMAdd0 = &*LLVMIt++;
257*4b209c5dSvporpo   auto *LLVMAdd1 = &*LLVMIt++;
258*4b209c5dSvporpo   auto *LLVMAdd2 = &*LLVMIt++;
259*4b209c5dSvporpo 
260*4b209c5dSvporpo   sandboxir::Context Ctx(C);
261*4b209c5dSvporpo   auto *F = Ctx.createFunction(LLVMF);
262*4b209c5dSvporpo   auto *BB = &*F->begin();
263*4b209c5dSvporpo   auto It = BB->begin();
264*4b209c5dSvporpo   auto *Add0 = cast<sandboxir::Instruction>(&*It++);
265*4b209c5dSvporpo   auto *Add1 = cast<sandboxir::Instruction>(&*It++);
266*4b209c5dSvporpo   auto *Add2 = cast<sandboxir::Instruction>(&*It++);
267*4b209c5dSvporpo 
268*4b209c5dSvporpo   sandboxir::Region Rgn(Ctx, *TTI);
269*4b209c5dSvporpo   const auto &SB = Rgn.getScoreboard();
270*4b209c5dSvporpo   EXPECT_EQ(SB.getAfterCost(), 0);
271*4b209c5dSvporpo   EXPECT_EQ(SB.getBeforeCost(), 0);
272*4b209c5dSvporpo 
273*4b209c5dSvporpo   auto GetCost = [this](llvm::Instruction *LLVMI) {
274*4b209c5dSvporpo     constexpr static TTI::TargetCostKind CostKind = TTI::TCK_RecipThroughput;
275*4b209c5dSvporpo     SmallVector<const llvm::Value *> Operands(LLVMI->operands());
276*4b209c5dSvporpo     return TTI->getInstructionCost(LLVMI, Operands, CostKind);
277*4b209c5dSvporpo   };
278*4b209c5dSvporpo   // Add `Add0` to the region, should be counted in "After".
279*4b209c5dSvporpo   Rgn.add(Add0);
280*4b209c5dSvporpo   EXPECT_EQ(SB.getBeforeCost(), 0);
281*4b209c5dSvporpo   EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd0));
282*4b209c5dSvporpo   // Same for `Add1`.
283*4b209c5dSvporpo   Rgn.add(Add1);
284*4b209c5dSvporpo   EXPECT_EQ(SB.getBeforeCost(), 0);
285*4b209c5dSvporpo   EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd0) + GetCost(LLVMAdd1));
286*4b209c5dSvporpo   // Remove `Add0`, should be subtracted from "After".
287*4b209c5dSvporpo   Rgn.remove(Add0);
288*4b209c5dSvporpo   EXPECT_EQ(SB.getBeforeCost(), 0);
289*4b209c5dSvporpo   EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd1));
290*4b209c5dSvporpo   // Remove `Add2` which was never in the region, should counted in "Before".
291*4b209c5dSvporpo   Rgn.remove(Add2);
292*4b209c5dSvporpo   EXPECT_EQ(SB.getBeforeCost(), GetCost(LLVMAdd2));
293*4b209c5dSvporpo   EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd1));
294*4b209c5dSvporpo }
295