xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/BPF/BPFAdjustOpt.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
1*e8d8bef9SDimitry Andric //===---------------- BPFAdjustOpt.cpp - Adjust Optimization --------------===//
2*e8d8bef9SDimitry Andric //
3*e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*e8d8bef9SDimitry Andric //
7*e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8*e8d8bef9SDimitry Andric //
9*e8d8bef9SDimitry Andric // Adjust optimization to make the code more kernel verifier friendly.
10*e8d8bef9SDimitry Andric //
11*e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
12*e8d8bef9SDimitry Andric 
13*e8d8bef9SDimitry Andric #include "BPF.h"
14*e8d8bef9SDimitry Andric #include "BPFCORE.h"
15*e8d8bef9SDimitry Andric #include "BPFTargetMachine.h"
16*e8d8bef9SDimitry Andric #include "llvm/IR/Instruction.h"
17*e8d8bef9SDimitry Andric #include "llvm/IR/Instructions.h"
18*e8d8bef9SDimitry Andric #include "llvm/IR/Module.h"
19*e8d8bef9SDimitry Andric #include "llvm/IR/Type.h"
20*e8d8bef9SDimitry Andric #include "llvm/IR/User.h"
21*e8d8bef9SDimitry Andric #include "llvm/IR/Value.h"
22*e8d8bef9SDimitry Andric #include "llvm/Pass.h"
23*e8d8bef9SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h"
24*e8d8bef9SDimitry Andric 
25*e8d8bef9SDimitry Andric #define DEBUG_TYPE "bpf-adjust-opt"
26*e8d8bef9SDimitry Andric 
27*e8d8bef9SDimitry Andric using namespace llvm;
28*e8d8bef9SDimitry Andric 
29*e8d8bef9SDimitry Andric static cl::opt<bool>
30*e8d8bef9SDimitry Andric     DisableBPFserializeICMP("bpf-disable-serialize-icmp", cl::Hidden,
31*e8d8bef9SDimitry Andric                             cl::desc("BPF: Disable Serializing ICMP insns."),
32*e8d8bef9SDimitry Andric                             cl::init(false));
33*e8d8bef9SDimitry Andric 
34*e8d8bef9SDimitry Andric static cl::opt<bool> DisableBPFavoidSpeculation(
35*e8d8bef9SDimitry Andric     "bpf-disable-avoid-speculation", cl::Hidden,
36*e8d8bef9SDimitry Andric     cl::desc("BPF: Disable Avoiding Speculative Code Motion."),
37*e8d8bef9SDimitry Andric     cl::init(false));
38*e8d8bef9SDimitry Andric 
39*e8d8bef9SDimitry Andric namespace {
40*e8d8bef9SDimitry Andric 
41*e8d8bef9SDimitry Andric class BPFAdjustOpt final : public ModulePass {
42*e8d8bef9SDimitry Andric public:
43*e8d8bef9SDimitry Andric   static char ID;
44*e8d8bef9SDimitry Andric 
45*e8d8bef9SDimitry Andric   BPFAdjustOpt() : ModulePass(ID) {}
46*e8d8bef9SDimitry Andric   bool runOnModule(Module &M) override;
47*e8d8bef9SDimitry Andric };
48*e8d8bef9SDimitry Andric 
49*e8d8bef9SDimitry Andric class BPFAdjustOptImpl {
50*e8d8bef9SDimitry Andric   struct PassThroughInfo {
51*e8d8bef9SDimitry Andric     Instruction *Input;
52*e8d8bef9SDimitry Andric     Instruction *UsedInst;
53*e8d8bef9SDimitry Andric     uint32_t OpIdx;
54*e8d8bef9SDimitry Andric     PassThroughInfo(Instruction *I, Instruction *U, uint32_t Idx)
55*e8d8bef9SDimitry Andric         : Input(I), UsedInst(U), OpIdx(Idx) {}
56*e8d8bef9SDimitry Andric   };
57*e8d8bef9SDimitry Andric 
58*e8d8bef9SDimitry Andric public:
59*e8d8bef9SDimitry Andric   BPFAdjustOptImpl(Module *M) : M(M) {}
60*e8d8bef9SDimitry Andric 
61*e8d8bef9SDimitry Andric   bool run();
62*e8d8bef9SDimitry Andric 
63*e8d8bef9SDimitry Andric private:
64*e8d8bef9SDimitry Andric   Module *M;
65*e8d8bef9SDimitry Andric   SmallVector<PassThroughInfo, 16> PassThroughs;
66*e8d8bef9SDimitry Andric 
67*e8d8bef9SDimitry Andric   void adjustBasicBlock(BasicBlock &BB);
68*e8d8bef9SDimitry Andric   bool serializeICMPCrossBB(BasicBlock &BB);
69*e8d8bef9SDimitry Andric   void adjustInst(Instruction &I);
70*e8d8bef9SDimitry Andric   bool serializeICMPInBB(Instruction &I);
71*e8d8bef9SDimitry Andric   bool avoidSpeculation(Instruction &I);
72*e8d8bef9SDimitry Andric   bool insertPassThrough();
73*e8d8bef9SDimitry Andric };
74*e8d8bef9SDimitry Andric 
75*e8d8bef9SDimitry Andric } // End anonymous namespace
76*e8d8bef9SDimitry Andric 
77*e8d8bef9SDimitry Andric char BPFAdjustOpt::ID = 0;
78*e8d8bef9SDimitry Andric INITIALIZE_PASS(BPFAdjustOpt, "bpf-adjust-opt", "BPF Adjust Optimization",
79*e8d8bef9SDimitry Andric                 false, false)
80*e8d8bef9SDimitry Andric 
81*e8d8bef9SDimitry Andric ModulePass *llvm::createBPFAdjustOpt() { return new BPFAdjustOpt(); }
82*e8d8bef9SDimitry Andric 
83*e8d8bef9SDimitry Andric bool BPFAdjustOpt::runOnModule(Module &M) { return BPFAdjustOptImpl(&M).run(); }
84*e8d8bef9SDimitry Andric 
85*e8d8bef9SDimitry Andric bool BPFAdjustOptImpl::run() {
86*e8d8bef9SDimitry Andric   for (Function &F : *M)
87*e8d8bef9SDimitry Andric     for (auto &BB : F) {
88*e8d8bef9SDimitry Andric       adjustBasicBlock(BB);
89*e8d8bef9SDimitry Andric       for (auto &I : BB)
90*e8d8bef9SDimitry Andric         adjustInst(I);
91*e8d8bef9SDimitry Andric     }
92*e8d8bef9SDimitry Andric 
93*e8d8bef9SDimitry Andric   return insertPassThrough();
94*e8d8bef9SDimitry Andric }
95*e8d8bef9SDimitry Andric 
96*e8d8bef9SDimitry Andric bool BPFAdjustOptImpl::insertPassThrough() {
97*e8d8bef9SDimitry Andric   for (auto &Info : PassThroughs) {
98*e8d8bef9SDimitry Andric     auto *CI = BPFCoreSharedInfo::insertPassThrough(
99*e8d8bef9SDimitry Andric         M, Info.UsedInst->getParent(), Info.Input, Info.UsedInst);
100*e8d8bef9SDimitry Andric     Info.UsedInst->setOperand(Info.OpIdx, CI);
101*e8d8bef9SDimitry Andric   }
102*e8d8bef9SDimitry Andric 
103*e8d8bef9SDimitry Andric   return !PassThroughs.empty();
104*e8d8bef9SDimitry Andric }
105*e8d8bef9SDimitry Andric 
106*e8d8bef9SDimitry Andric // To avoid combining conditionals in the same basic block by
107*e8d8bef9SDimitry Andric // instrcombine optimization.
108*e8d8bef9SDimitry Andric bool BPFAdjustOptImpl::serializeICMPInBB(Instruction &I) {
109*e8d8bef9SDimitry Andric   // For:
110*e8d8bef9SDimitry Andric   //   comp1 = icmp <opcode> ...;
111*e8d8bef9SDimitry Andric   //   comp2 = icmp <opcode> ...;
112*e8d8bef9SDimitry Andric   //   ... or comp1 comp2 ...
113*e8d8bef9SDimitry Andric   // changed to:
114*e8d8bef9SDimitry Andric   //   comp1 = icmp <opcode> ...;
115*e8d8bef9SDimitry Andric   //   comp2 = icmp <opcode> ...;
116*e8d8bef9SDimitry Andric   //   new_comp1 = __builtin_bpf_passthrough(seq_num, comp1)
117*e8d8bef9SDimitry Andric   //   ... or new_comp1 comp2 ...
118*e8d8bef9SDimitry Andric   if (I.getOpcode() != Instruction::Or)
119*e8d8bef9SDimitry Andric     return false;
120*e8d8bef9SDimitry Andric   auto *Icmp1 = dyn_cast<ICmpInst>(I.getOperand(0));
121*e8d8bef9SDimitry Andric   if (!Icmp1)
122*e8d8bef9SDimitry Andric     return false;
123*e8d8bef9SDimitry Andric   auto *Icmp2 = dyn_cast<ICmpInst>(I.getOperand(1));
124*e8d8bef9SDimitry Andric   if (!Icmp2)
125*e8d8bef9SDimitry Andric     return false;
126*e8d8bef9SDimitry Andric 
127*e8d8bef9SDimitry Andric   Value *Icmp1Op0 = Icmp1->getOperand(0);
128*e8d8bef9SDimitry Andric   Value *Icmp2Op0 = Icmp2->getOperand(0);
129*e8d8bef9SDimitry Andric   if (Icmp1Op0 != Icmp2Op0)
130*e8d8bef9SDimitry Andric     return false;
131*e8d8bef9SDimitry Andric 
132*e8d8bef9SDimitry Andric   // Now we got two icmp instructions which feed into
133*e8d8bef9SDimitry Andric   // an "or" instruction.
134*e8d8bef9SDimitry Andric   PassThroughInfo Info(Icmp1, &I, 0);
135*e8d8bef9SDimitry Andric   PassThroughs.push_back(Info);
136*e8d8bef9SDimitry Andric   return true;
137*e8d8bef9SDimitry Andric }
138*e8d8bef9SDimitry Andric 
139*e8d8bef9SDimitry Andric // To avoid combining conditionals in the same basic block by
140*e8d8bef9SDimitry Andric // instrcombine optimization.
141*e8d8bef9SDimitry Andric bool BPFAdjustOptImpl::serializeICMPCrossBB(BasicBlock &BB) {
142*e8d8bef9SDimitry Andric   // For:
143*e8d8bef9SDimitry Andric   //   B1:
144*e8d8bef9SDimitry Andric   //     comp1 = icmp <opcode> ...;
145*e8d8bef9SDimitry Andric   //     if (comp1) goto B2 else B3;
146*e8d8bef9SDimitry Andric   //   B2:
147*e8d8bef9SDimitry Andric   //     comp2 = icmp <opcode> ...;
148*e8d8bef9SDimitry Andric   //     if (comp2) goto B4 else B5;
149*e8d8bef9SDimitry Andric   //   B4:
150*e8d8bef9SDimitry Andric   //     ...
151*e8d8bef9SDimitry Andric   // changed to:
152*e8d8bef9SDimitry Andric   //   B1:
153*e8d8bef9SDimitry Andric   //     comp1 = icmp <opcode> ...;
154*e8d8bef9SDimitry Andric   //     comp1 = __builtin_bpf_passthrough(seq_num, comp1);
155*e8d8bef9SDimitry Andric   //     if (comp1) goto B2 else B3;
156*e8d8bef9SDimitry Andric   //   B2:
157*e8d8bef9SDimitry Andric   //     comp2 = icmp <opcode> ...;
158*e8d8bef9SDimitry Andric   //     if (comp2) goto B4 else B5;
159*e8d8bef9SDimitry Andric   //   B4:
160*e8d8bef9SDimitry Andric   //     ...
161*e8d8bef9SDimitry Andric 
162*e8d8bef9SDimitry Andric   // Check basic predecessors, if two of them (say B1, B2) are using
163*e8d8bef9SDimitry Andric   // icmp instructions to generate conditions and one is the predesessor
164*e8d8bef9SDimitry Andric   // of another (e.g., B1 is the predecessor of B2). Add a passthrough
165*e8d8bef9SDimitry Andric   // barrier after icmp inst of block B1.
166*e8d8bef9SDimitry Andric   BasicBlock *B2 = BB.getSinglePredecessor();
167*e8d8bef9SDimitry Andric   if (!B2)
168*e8d8bef9SDimitry Andric     return false;
169*e8d8bef9SDimitry Andric 
170*e8d8bef9SDimitry Andric   BasicBlock *B1 = B2->getSinglePredecessor();
171*e8d8bef9SDimitry Andric   if (!B1)
172*e8d8bef9SDimitry Andric     return false;
173*e8d8bef9SDimitry Andric 
174*e8d8bef9SDimitry Andric   Instruction *TI = B2->getTerminator();
175*e8d8bef9SDimitry Andric   auto *BI = dyn_cast<BranchInst>(TI);
176*e8d8bef9SDimitry Andric   if (!BI || !BI->isConditional())
177*e8d8bef9SDimitry Andric     return false;
178*e8d8bef9SDimitry Andric   auto *Cond = dyn_cast<ICmpInst>(BI->getCondition());
179*e8d8bef9SDimitry Andric   if (!Cond || B2->getFirstNonPHI() != Cond)
180*e8d8bef9SDimitry Andric     return false;
181*e8d8bef9SDimitry Andric   Value *B2Op0 = Cond->getOperand(0);
182*e8d8bef9SDimitry Andric   auto Cond2Op = Cond->getPredicate();
183*e8d8bef9SDimitry Andric 
184*e8d8bef9SDimitry Andric   TI = B1->getTerminator();
185*e8d8bef9SDimitry Andric   BI = dyn_cast<BranchInst>(TI);
186*e8d8bef9SDimitry Andric   if (!BI || !BI->isConditional())
187*e8d8bef9SDimitry Andric     return false;
188*e8d8bef9SDimitry Andric   Cond = dyn_cast<ICmpInst>(BI->getCondition());
189*e8d8bef9SDimitry Andric   if (!Cond)
190*e8d8bef9SDimitry Andric     return false;
191*e8d8bef9SDimitry Andric   Value *B1Op0 = Cond->getOperand(0);
192*e8d8bef9SDimitry Andric   auto Cond1Op = Cond->getPredicate();
193*e8d8bef9SDimitry Andric 
194*e8d8bef9SDimitry Andric   if (B1Op0 != B2Op0)
195*e8d8bef9SDimitry Andric     return false;
196*e8d8bef9SDimitry Andric 
197*e8d8bef9SDimitry Andric   if (Cond1Op == ICmpInst::ICMP_SGT || Cond1Op == ICmpInst::ICMP_SGE) {
198*e8d8bef9SDimitry Andric     if (Cond2Op != ICmpInst::ICMP_SLT && Cond1Op != ICmpInst::ICMP_SLE)
199*e8d8bef9SDimitry Andric       return false;
200*e8d8bef9SDimitry Andric   } else if (Cond1Op == ICmpInst::ICMP_SLT || Cond1Op == ICmpInst::ICMP_SLE) {
201*e8d8bef9SDimitry Andric     if (Cond2Op != ICmpInst::ICMP_SGT && Cond1Op != ICmpInst::ICMP_SGE)
202*e8d8bef9SDimitry Andric       return false;
203*e8d8bef9SDimitry Andric   } else {
204*e8d8bef9SDimitry Andric     return false;
205*e8d8bef9SDimitry Andric   }
206*e8d8bef9SDimitry Andric 
207*e8d8bef9SDimitry Andric   PassThroughInfo Info(Cond, BI, 0);
208*e8d8bef9SDimitry Andric   PassThroughs.push_back(Info);
209*e8d8bef9SDimitry Andric 
210*e8d8bef9SDimitry Andric   return true;
211*e8d8bef9SDimitry Andric }
212*e8d8bef9SDimitry Andric 
213*e8d8bef9SDimitry Andric // To avoid speculative hoisting certain computations out of
214*e8d8bef9SDimitry Andric // a basic block.
215*e8d8bef9SDimitry Andric bool BPFAdjustOptImpl::avoidSpeculation(Instruction &I) {
216*e8d8bef9SDimitry Andric   if (auto *LdInst = dyn_cast<LoadInst>(&I)) {
217*e8d8bef9SDimitry Andric     if (auto *GV = dyn_cast<GlobalVariable>(LdInst->getOperand(0))) {
218*e8d8bef9SDimitry Andric       if (GV->hasAttribute(BPFCoreSharedInfo::AmaAttr) ||
219*e8d8bef9SDimitry Andric           GV->hasAttribute(BPFCoreSharedInfo::TypeIdAttr))
220*e8d8bef9SDimitry Andric         return false;
221*e8d8bef9SDimitry Andric     }
222*e8d8bef9SDimitry Andric   }
223*e8d8bef9SDimitry Andric 
224*e8d8bef9SDimitry Andric   if (!isa<LoadInst>(&I) && !isa<CallInst>(&I))
225*e8d8bef9SDimitry Andric     return false;
226*e8d8bef9SDimitry Andric 
227*e8d8bef9SDimitry Andric   // For:
228*e8d8bef9SDimitry Andric   //   B1:
229*e8d8bef9SDimitry Andric   //     var = ...
230*e8d8bef9SDimitry Andric   //     ...
231*e8d8bef9SDimitry Andric   //     /* icmp may not be in the same block as var = ... */
232*e8d8bef9SDimitry Andric   //     comp1 = icmp <opcode> var, <const>;
233*e8d8bef9SDimitry Andric   //     if (comp1) goto B2 else B3;
234*e8d8bef9SDimitry Andric   //   B2:
235*e8d8bef9SDimitry Andric   //     ... var ...
236*e8d8bef9SDimitry Andric   // change to:
237*e8d8bef9SDimitry Andric   //   B1:
238*e8d8bef9SDimitry Andric   //     var = ...
239*e8d8bef9SDimitry Andric   //     ...
240*e8d8bef9SDimitry Andric   //     /* icmp may not be in the same block as var = ... */
241*e8d8bef9SDimitry Andric   //     comp1 = icmp <opcode> var, <const>;
242*e8d8bef9SDimitry Andric   //     if (comp1) goto B2 else B3;
243*e8d8bef9SDimitry Andric   //   B2:
244*e8d8bef9SDimitry Andric   //     var = __builtin_bpf_passthrough(seq_num, var);
245*e8d8bef9SDimitry Andric   //     ... var ...
246*e8d8bef9SDimitry Andric   bool isCandidate = false;
247*e8d8bef9SDimitry Andric   SmallVector<PassThroughInfo, 4> Candidates;
248*e8d8bef9SDimitry Andric   for (User *U : I.users()) {
249*e8d8bef9SDimitry Andric     Instruction *Inst = dyn_cast<Instruction>(U);
250*e8d8bef9SDimitry Andric     if (!Inst)
251*e8d8bef9SDimitry Andric       continue;
252*e8d8bef9SDimitry Andric 
253*e8d8bef9SDimitry Andric     // May cover a little bit more than the
254*e8d8bef9SDimitry Andric     // above pattern.
255*e8d8bef9SDimitry Andric     if (auto *Icmp1 = dyn_cast<ICmpInst>(Inst)) {
256*e8d8bef9SDimitry Andric       Value *Icmp1Op1 = Icmp1->getOperand(1);
257*e8d8bef9SDimitry Andric       if (!isa<Constant>(Icmp1Op1))
258*e8d8bef9SDimitry Andric         return false;
259*e8d8bef9SDimitry Andric       isCandidate = true;
260*e8d8bef9SDimitry Andric       continue;
261*e8d8bef9SDimitry Andric     }
262*e8d8bef9SDimitry Andric 
263*e8d8bef9SDimitry Andric     // Ignore the use in the same basic block as the definition.
264*e8d8bef9SDimitry Andric     if (Inst->getParent() == I.getParent())
265*e8d8bef9SDimitry Andric       continue;
266*e8d8bef9SDimitry Andric 
267*e8d8bef9SDimitry Andric     // use in a different basic block, If there is a call or
268*e8d8bef9SDimitry Andric     // load/store insn before this instruction in this basic
269*e8d8bef9SDimitry Andric     // block. Most likely it cannot be hoisted out. Skip it.
270*e8d8bef9SDimitry Andric     for (auto &I2 : *Inst->getParent()) {
271*e8d8bef9SDimitry Andric       if (dyn_cast<CallInst>(&I2))
272*e8d8bef9SDimitry Andric         return false;
273*e8d8bef9SDimitry Andric       if (dyn_cast<LoadInst>(&I2) || dyn_cast<StoreInst>(&I2))
274*e8d8bef9SDimitry Andric         return false;
275*e8d8bef9SDimitry Andric       if (&I2 == Inst)
276*e8d8bef9SDimitry Andric         break;
277*e8d8bef9SDimitry Andric     }
278*e8d8bef9SDimitry Andric 
279*e8d8bef9SDimitry Andric     // It should be used in a GEP or a simple arithmetic like
280*e8d8bef9SDimitry Andric     // ZEXT/SEXT which is used for GEP.
281*e8d8bef9SDimitry Andric     if (Inst->getOpcode() == Instruction::ZExt ||
282*e8d8bef9SDimitry Andric         Inst->getOpcode() == Instruction::SExt) {
283*e8d8bef9SDimitry Andric       PassThroughInfo Info(&I, Inst, 0);
284*e8d8bef9SDimitry Andric       Candidates.push_back(Info);
285*e8d8bef9SDimitry Andric     } else if (auto *GI = dyn_cast<GetElementPtrInst>(Inst)) {
286*e8d8bef9SDimitry Andric       // traverse GEP inst to find Use operand index
287*e8d8bef9SDimitry Andric       unsigned i, e;
288*e8d8bef9SDimitry Andric       for (i = 1, e = GI->getNumOperands(); i != e; ++i) {
289*e8d8bef9SDimitry Andric         Value *V = GI->getOperand(i);
290*e8d8bef9SDimitry Andric         if (V == &I)
291*e8d8bef9SDimitry Andric           break;
292*e8d8bef9SDimitry Andric       }
293*e8d8bef9SDimitry Andric       if (i == e)
294*e8d8bef9SDimitry Andric         continue;
295*e8d8bef9SDimitry Andric 
296*e8d8bef9SDimitry Andric       PassThroughInfo Info(&I, GI, i);
297*e8d8bef9SDimitry Andric       Candidates.push_back(Info);
298*e8d8bef9SDimitry Andric     }
299*e8d8bef9SDimitry Andric   }
300*e8d8bef9SDimitry Andric 
301*e8d8bef9SDimitry Andric   if (!isCandidate || Candidates.empty())
302*e8d8bef9SDimitry Andric     return false;
303*e8d8bef9SDimitry Andric 
304*e8d8bef9SDimitry Andric   llvm::append_range(PassThroughs, Candidates);
305*e8d8bef9SDimitry Andric   return true;
306*e8d8bef9SDimitry Andric }
307*e8d8bef9SDimitry Andric 
308*e8d8bef9SDimitry Andric void BPFAdjustOptImpl::adjustBasicBlock(BasicBlock &BB) {
309*e8d8bef9SDimitry Andric   if (!DisableBPFserializeICMP && serializeICMPCrossBB(BB))
310*e8d8bef9SDimitry Andric     return;
311*e8d8bef9SDimitry Andric }
312*e8d8bef9SDimitry Andric 
313*e8d8bef9SDimitry Andric void BPFAdjustOptImpl::adjustInst(Instruction &I) {
314*e8d8bef9SDimitry Andric   if (!DisableBPFserializeICMP && serializeICMPInBB(I))
315*e8d8bef9SDimitry Andric     return;
316*e8d8bef9SDimitry Andric   if (!DisableBPFavoidSpeculation && avoidSpeculation(I))
317*e8d8bef9SDimitry Andric     return;
318*e8d8bef9SDimitry Andric }
319*e8d8bef9SDimitry Andric 
320*e8d8bef9SDimitry Andric PreservedAnalyses BPFAdjustOptPass::run(Module &M, ModuleAnalysisManager &AM) {
321*e8d8bef9SDimitry Andric   return BPFAdjustOptImpl(&M).run() ? PreservedAnalyses::none()
322*e8d8bef9SDimitry Andric                                     : PreservedAnalyses::all();
323*e8d8bef9SDimitry Andric }
324