1e1649c31SDevang Patel //===- LexicalScopes.cpp - Collecting lexical scope info ------------------===//
2e1649c31SDevang Patel //
3e1649c31SDevang Patel //                     The LLVM Compiler Infrastructure
4e1649c31SDevang Patel //
5e1649c31SDevang Patel // This file is distributed under the University of Illinois Open Source
6e1649c31SDevang Patel // License. See LICENSE.TXT for details.
7e1649c31SDevang Patel //
8e1649c31SDevang Patel //===----------------------------------------------------------------------===//
9e1649c31SDevang Patel //
10e1649c31SDevang Patel // This file implements LexicalScopes analysis.
11e1649c31SDevang Patel //
12e1649c31SDevang Patel // This pass collects lexical scope information and maps machine instructions
13e1649c31SDevang Patel // to respective lexical scopes.
14e1649c31SDevang Patel //
15e1649c31SDevang Patel //===----------------------------------------------------------------------===//
16e1649c31SDevang Patel 
176bda14b3SChandler Carruth #include "llvm/CodeGen/LexicalScopes.h"
185db84df7SEugene Zelenko #include "llvm/ADT/DenseMap.h"
195db84df7SEugene Zelenko #include "llvm/ADT/SmallVector.h"
205db84df7SEugene Zelenko #include "llvm/CodeGen/MachineBasicBlock.h"
21e1649c31SDevang Patel #include "llvm/CodeGen/MachineFunction.h"
22e1649c31SDevang Patel #include "llvm/CodeGen/MachineInstr.h"
235db84df7SEugene Zelenko #include "llvm/IR/DebugInfoMetadata.h"
245db84df7SEugene Zelenko #include "llvm/IR/Metadata.h"
255db84df7SEugene Zelenko #include "llvm/Support/Casting.h"
265db84df7SEugene Zelenko #include "llvm/Support/Compiler.h"
27e1649c31SDevang Patel #include "llvm/Support/Debug.h"
285db84df7SEugene Zelenko #include "llvm/Support/raw_ostream.h"
295db84df7SEugene Zelenko #include <cassert>
305db84df7SEugene Zelenko #include <string>
315db84df7SEugene Zelenko #include <tuple>
325db84df7SEugene Zelenko #include <utility>
335db84df7SEugene Zelenko 
34e1649c31SDevang Patel using namespace llvm;
35e1649c31SDevang Patel 
361b9dde08SChandler Carruth #define DEBUG_TYPE "lexicalscopes"
371b9dde08SChandler Carruth 
38b7dee8a6SEric Christopher /// reset - Reset the instance so that it's prepared for another function.
39b7dee8a6SEric Christopher void LexicalScopes::reset() {
40c0196b1bSCraig Topper   MF = nullptr;
41c0196b1bSCraig Topper   CurrentFnLexicalScope = nullptr;
422f143e0cSDavid Blaikie   LexicalScopeMap.clear();
432f143e0cSDavid Blaikie   AbstractScopeMap.clear();
44e1649c31SDevang Patel   InlinedLexicalScopeMap.clear();
45e1649c31SDevang Patel   AbstractScopesList.clear();
46e1649c31SDevang Patel }
47e1649c31SDevang Patel 
48e1649c31SDevang Patel /// initialize - Scan machine function and constuct lexical scope nest.
49e1649c31SDevang Patel void LexicalScopes::initialize(const MachineFunction &Fn) {
50*e018bbd8SWolfgang Pieb   reset();
5176b5b749STeresa Johnson   // Don't attempt any lexical scope creation for a NoDebug compile unit.
5276b5b749STeresa Johnson   if (Fn.getFunction()->getSubprogram()->getUnit()->getEmissionKind() ==
5376b5b749STeresa Johnson       DICompileUnit::NoDebug)
5476b5b749STeresa Johnson     return;
55e1649c31SDevang Patel   MF = &Fn;
56e1649c31SDevang Patel   SmallVector<InsnRange, 4> MIRanges;
57e1649c31SDevang Patel   DenseMap<const MachineInstr *, LexicalScope *> MI2ScopeMap;
58e1649c31SDevang Patel   extractLexicalScopes(MIRanges, MI2ScopeMap);
59e1649c31SDevang Patel   if (CurrentFnLexicalScope) {
60e1649c31SDevang Patel     constructScopeNest(CurrentFnLexicalScope);
61e1649c31SDevang Patel     assignInstructionRanges(MIRanges, MI2ScopeMap);
62e1649c31SDevang Patel   }
63e1649c31SDevang Patel }
64e1649c31SDevang Patel 
65e1649c31SDevang Patel /// extractLexicalScopes - Extract instruction ranges for each lexical scopes
66e1649c31SDevang Patel /// for the given machine function.
676211e4b9SEric Christopher void LexicalScopes::extractLexicalScopes(
686211e4b9SEric Christopher     SmallVectorImpl<InsnRange> &MIRanges,
69e1649c31SDevang Patel     DenseMap<const MachineInstr *, LexicalScope *> &MI2ScopeMap) {
70e1649c31SDevang Patel   // Scan each instruction and create scopes. First build working set of scopes.
7141b977dfSAlexey Samsonov   for (const auto &MBB : *MF) {
72c0196b1bSCraig Topper     const MachineInstr *RangeBeginMI = nullptr;
73c0196b1bSCraig Topper     const MachineInstr *PrevMI = nullptr;
74a9308c49SDuncan P. N. Exon Smith     const DILocation *PrevDL = nullptr;
75f74bde67SAlexey Samsonov     for (const auto &MInsn : MBB) {
76e1649c31SDevang Patel       // Check if instruction has valid location information.
77a9308c49SDuncan P. N. Exon Smith       const DILocation *MIDL = MInsn.getDebugLoc();
789dffcd04SDuncan P. N. Exon Smith       if (!MIDL) {
79f74bde67SAlexey Samsonov         PrevMI = &MInsn;
80e1649c31SDevang Patel         continue;
81e1649c31SDevang Patel       }
82e1649c31SDevang Patel 
83e1649c31SDevang Patel       // If scope has not changed then skip this instruction.
84e1649c31SDevang Patel       if (MIDL == PrevDL) {
85f74bde67SAlexey Samsonov         PrevMI = &MInsn;
86e1649c31SDevang Patel         continue;
87e1649c31SDevang Patel       }
88e1649c31SDevang Patel 
89fb31da13SAdrian Prantl       // Ignore DBG_VALUE and similar instruction that do not contribute to any
90fb31da13SAdrian Prantl       // instruction in the output.
91fb31da13SAdrian Prantl       if (MInsn.isMetaInstruction())
92e1649c31SDevang Patel         continue;
93e1649c31SDevang Patel 
94e1649c31SDevang Patel       if (RangeBeginMI) {
95e1649c31SDevang Patel         // If we have already seen a beginning of an instruction range and
96e1649c31SDevang Patel         // current instruction scope does not match scope of first instruction
97e1649c31SDevang Patel         // in this range then create a new instruction range.
98e1649c31SDevang Patel         InsnRange R(RangeBeginMI, PrevMI);
99e1649c31SDevang Patel         MI2ScopeMap[RangeBeginMI] = getOrCreateLexicalScope(PrevDL);
100e1649c31SDevang Patel         MIRanges.push_back(R);
101e1649c31SDevang Patel       }
102e1649c31SDevang Patel 
103e1649c31SDevang Patel       // This is a beginning of a new instruction range.
104f74bde67SAlexey Samsonov       RangeBeginMI = &MInsn;
105e1649c31SDevang Patel 
106e1649c31SDevang Patel       // Reset previous markers.
107f74bde67SAlexey Samsonov       PrevMI = &MInsn;
108e1649c31SDevang Patel       PrevDL = MIDL;
109e1649c31SDevang Patel     }
110e1649c31SDevang Patel 
111e1649c31SDevang Patel     // Create last instruction range.
1129dffcd04SDuncan P. N. Exon Smith     if (RangeBeginMI && PrevMI && PrevDL) {
113e1649c31SDevang Patel       InsnRange R(RangeBeginMI, PrevMI);
114e1649c31SDevang Patel       MIRanges.push_back(R);
115e1649c31SDevang Patel       MI2ScopeMap[RangeBeginMI] = getOrCreateLexicalScope(PrevDL);
116e1649c31SDevang Patel     }
117e1649c31SDevang Patel   }
118e1649c31SDevang Patel }
119e1649c31SDevang Patel 
120e1649c31SDevang Patel /// findLexicalScope - Find lexical scope, either regular or inlined, for the
121e1649c31SDevang Patel /// given DebugLoc. Return NULL if not found.
122a9308c49SDuncan P. N. Exon Smith LexicalScope *LexicalScopes::findLexicalScope(const DILocation *DL) {
123a9308c49SDuncan P. N. Exon Smith   DILocalScope *Scope = DL->getScope();
1246211e4b9SEric Christopher   if (!Scope)
125c0196b1bSCraig Topper     return nullptr;
1266647b830SEric Christopher 
1276647b830SEric Christopher   // The scope that we were created with could have an extra file - which
1286647b830SEric Christopher   // isn't what we care about in this case.
129a5ba9914SAmjad Aboud   Scope = Scope->getNonLexicalBlockFileScope();
1306647b830SEric Christopher 
1315a227fffSDuncan P. N. Exon Smith   if (auto *IA = DL->getInlinedAt()) {
1329b8c8cdaSDavid Blaikie     auto I = InlinedLexicalScopeMap.find(std::make_pair(Scope, IA));
1339b8c8cdaSDavid Blaikie     return I != InlinedLexicalScopeMap.end() ? &I->second : nullptr;
1349b8c8cdaSDavid Blaikie   }
1352f143e0cSDavid Blaikie   return findLexicalScope(Scope);
136e1649c31SDevang Patel }
137e1649c31SDevang Patel 
138e1649c31SDevang Patel /// getOrCreateLexicalScope - Find lexical scope for the given DebugLoc. If
139e1649c31SDevang Patel /// not available then create new lexical scope.
140a9308c49SDuncan P. N. Exon Smith LexicalScope *LexicalScopes::getOrCreateLexicalScope(const DILocalScope *Scope,
141a9308c49SDuncan P. N. Exon Smith                                                      const DILocation *IA) {
14282eba746SDuncan P. N. Exon Smith   if (IA) {
14376b5b749STeresa Johnson     // Skip scopes inlined from a NoDebug compile unit.
14476b5b749STeresa Johnson     if (Scope->getSubprogram()->getUnit()->getEmissionKind() ==
14576b5b749STeresa Johnson         DICompileUnit::NoDebug)
14676b5b749STeresa Johnson       return getOrCreateLexicalScope(IA);
147e1649c31SDevang Patel     // Create an abstract scope for inlined function.
148e1649c31SDevang Patel     getOrCreateAbstractScope(Scope);
149e1649c31SDevang Patel     // Create an inlined scope for inlined function.
15082eba746SDuncan P. N. Exon Smith     return getOrCreateInlinedScope(Scope, IA);
151e1649c31SDevang Patel   }
152e1649c31SDevang Patel 
153e1649c31SDevang Patel   return getOrCreateRegularScope(Scope);
154e1649c31SDevang Patel }
155e1649c31SDevang Patel 
156e1649c31SDevang Patel /// getOrCreateRegularScope - Find or create a regular lexical scope.
15733af7a8fSDuncan P. N. Exon Smith LexicalScope *
158a9308c49SDuncan P. N. Exon Smith LexicalScopes::getOrCreateRegularScope(const DILocalScope *Scope) {
159a5ba9914SAmjad Aboud   assert(Scope && "Invalid Scope encoding!");
160a5ba9914SAmjad Aboud   Scope = Scope->getNonLexicalBlockFileScope();
1616647b830SEric Christopher 
1622f143e0cSDavid Blaikie   auto I = LexicalScopeMap.find(Scope);
1632f143e0cSDavid Blaikie   if (I != LexicalScopeMap.end())
1642f143e0cSDavid Blaikie     return &I->second;
165e1649c31SDevang Patel 
166a9308c49SDuncan P. N. Exon Smith   // FIXME: Should the following dyn_cast be DILexicalBlock?
167c0196b1bSCraig Topper   LexicalScope *Parent = nullptr;
168a9308c49SDuncan P. N. Exon Smith   if (auto *Block = dyn_cast<DILexicalBlockBase>(Scope))
16982eba746SDuncan P. N. Exon Smith     Parent = getOrCreateLexicalScope(Block->getScope());
170f17583eeSAaron Ballman   I = LexicalScopeMap.emplace(std::piecewise_construct,
171f17583eeSAaron Ballman                               std::forward_as_tuple(Scope),
17233af7a8fSDuncan P. N. Exon Smith                               std::forward_as_tuple(Parent, Scope, nullptr,
17333af7a8fSDuncan P. N. Exon Smith                                                     false)).first;
1742f143e0cSDavid Blaikie 
1753dfe4788SDavid Blaikie   if (!Parent) {
176a9308c49SDuncan P. N. Exon Smith     assert(cast<DISubprogram>(Scope)->describes(MF->getFunction()));
1773dfe4788SDavid Blaikie     assert(!CurrentFnLexicalScope);
1782f143e0cSDavid Blaikie     CurrentFnLexicalScope = &I->second;
1793dfe4788SDavid Blaikie   }
180e1649c31SDevang Patel 
1812f143e0cSDavid Blaikie   return &I->second;
182e1649c31SDevang Patel }
183e1649c31SDevang Patel 
184e1649c31SDevang Patel /// getOrCreateInlinedScope - Find or create an inlined lexical scope.
18533af7a8fSDuncan P. N. Exon Smith LexicalScope *
186a9308c49SDuncan P. N. Exon Smith LexicalScopes::getOrCreateInlinedScope(const DILocalScope *Scope,
187a9308c49SDuncan P. N. Exon Smith                                        const DILocation *InlinedAt) {
188a5ba9914SAmjad Aboud   assert(Scope && "Invalid Scope encoding!");
189a5ba9914SAmjad Aboud   Scope = Scope->getNonLexicalBlockFileScope();
190a9308c49SDuncan P. N. Exon Smith   std::pair<const DILocalScope *, const DILocation *> P(Scope, InlinedAt);
1919b8c8cdaSDavid Blaikie   auto I = InlinedLexicalScopeMap.find(P);
1929b8c8cdaSDavid Blaikie   if (I != InlinedLexicalScopeMap.end())
1932f143e0cSDavid Blaikie     return &I->second;
194e1649c31SDevang Patel 
1959b8c8cdaSDavid Blaikie   LexicalScope *Parent;
196a9308c49SDuncan P. N. Exon Smith   if (auto *Block = dyn_cast<DILexicalBlockBase>(Scope))
19733af7a8fSDuncan P. N. Exon Smith     Parent = getOrCreateInlinedScope(Block->getScope(), InlinedAt);
1989b8c8cdaSDavid Blaikie   else
19933af7a8fSDuncan P. N. Exon Smith     Parent = getOrCreateLexicalScope(InlinedAt);
2009b8c8cdaSDavid Blaikie 
20114303d18SEric Christopher   I = InlinedLexicalScopeMap
20214303d18SEric Christopher           .emplace(std::piecewise_construct, std::forward_as_tuple(P),
20314303d18SEric Christopher                    std::forward_as_tuple(Parent, Scope, InlinedAt, false))
204f17583eeSAaron Ballman           .first;
2052f143e0cSDavid Blaikie   return &I->second;
206e1649c31SDevang Patel }
207e1649c31SDevang Patel 
208e1649c31SDevang Patel /// getOrCreateAbstractScope - Find or create an abstract lexical scope.
20933af7a8fSDuncan P. N. Exon Smith LexicalScope *
210a9308c49SDuncan P. N. Exon Smith LexicalScopes::getOrCreateAbstractScope(const DILocalScope *Scope) {
21133af7a8fSDuncan P. N. Exon Smith   assert(Scope && "Invalid Scope encoding!");
212a5ba9914SAmjad Aboud   Scope = Scope->getNonLexicalBlockFileScope();
213ea862267SDavid Blaikie   auto I = AbstractScopeMap.find(Scope);
2142f143e0cSDavid Blaikie   if (I != AbstractScopeMap.end())
2152f143e0cSDavid Blaikie     return &I->second;
216e1649c31SDevang Patel 
217a9308c49SDuncan P. N. Exon Smith   // FIXME: Should the following isa be DILexicalBlock?
218c0196b1bSCraig Topper   LexicalScope *Parent = nullptr;
219a9308c49SDuncan P. N. Exon Smith   if (auto *Block = dyn_cast<DILexicalBlockBase>(Scope))
22033af7a8fSDuncan P. N. Exon Smith     Parent = getOrCreateAbstractScope(Block->getScope());
22133af7a8fSDuncan P. N. Exon Smith 
2222f143e0cSDavid Blaikie   I = AbstractScopeMap.emplace(std::piecewise_construct,
223ea862267SDavid Blaikie                                std::forward_as_tuple(Scope),
224ea862267SDavid Blaikie                                std::forward_as_tuple(Parent, Scope,
2252f143e0cSDavid Blaikie                                                      nullptr, true)).first;
226a9308c49SDuncan P. N. Exon Smith   if (isa<DISubprogram>(Scope))
2272f143e0cSDavid Blaikie     AbstractScopesList.push_back(&I->second);
2282f143e0cSDavid Blaikie   return &I->second;
229e1649c31SDevang Patel }
230e1649c31SDevang Patel 
231e1649c31SDevang Patel /// constructScopeNest
232e1649c31SDevang Patel void LexicalScopes::constructScopeNest(LexicalScope *Scope) {
233e1649c31SDevang Patel   assert(Scope && "Unable to calculate scope dominance graph!");
234e1649c31SDevang Patel   SmallVector<LexicalScope *, 4> WorkStack;
235e1649c31SDevang Patel   WorkStack.push_back(Scope);
236e1649c31SDevang Patel   unsigned Counter = 0;
237e1649c31SDevang Patel   while (!WorkStack.empty()) {
238e1649c31SDevang Patel     LexicalScope *WS = WorkStack.back();
23980170e54SCraig Topper     const SmallVectorImpl<LexicalScope *> &Children = WS->getChildren();
240e1649c31SDevang Patel     bool visitedChildren = false;
24116b2ace0SAdrian Prantl     for (auto &ChildScope : Children)
242e1649c31SDevang Patel       if (!ChildScope->getDFSOut()) {
243e1649c31SDevang Patel         WorkStack.push_back(ChildScope);
244e1649c31SDevang Patel         visitedChildren = true;
245e1649c31SDevang Patel         ChildScope->setDFSIn(++Counter);
246e1649c31SDevang Patel         break;
247e1649c31SDevang Patel       }
248e1649c31SDevang Patel     if (!visitedChildren) {
249e1649c31SDevang Patel       WorkStack.pop_back();
250e1649c31SDevang Patel       WS->setDFSOut(++Counter);
251e1649c31SDevang Patel     }
252e1649c31SDevang Patel   }
253e1649c31SDevang Patel }
254e1649c31SDevang Patel 
255784077ebSDevang Patel /// assignInstructionRanges - Find ranges of instructions covered by each
256784077ebSDevang Patel /// lexical scope.
2576211e4b9SEric Christopher void LexicalScopes::assignInstructionRanges(
2586211e4b9SEric Christopher     SmallVectorImpl<InsnRange> &MIRanges,
2596211e4b9SEric Christopher     DenseMap<const MachineInstr *, LexicalScope *> &MI2ScopeMap) {
260c0196b1bSCraig Topper   LexicalScope *PrevLexicalScope = nullptr;
26116b2ace0SAdrian Prantl   for (const auto &R : MIRanges) {
262e1649c31SDevang Patel     LexicalScope *S = MI2ScopeMap.lookup(R.first);
263e1649c31SDevang Patel     assert(S && "Lost LexicalScope for a machine instruction!");
264e1649c31SDevang Patel     if (PrevLexicalScope && !PrevLexicalScope->dominates(S))
265e1649c31SDevang Patel       PrevLexicalScope->closeInsnRange(S);
266e1649c31SDevang Patel     S->openInsnRange(R.first);
267e1649c31SDevang Patel     S->extendInsnRange(R.second);
268e1649c31SDevang Patel     PrevLexicalScope = S;
269e1649c31SDevang Patel   }
270e1649c31SDevang Patel 
271e1649c31SDevang Patel   if (PrevLexicalScope)
272e1649c31SDevang Patel     PrevLexicalScope->closeInsnRange();
273e1649c31SDevang Patel }
274e1649c31SDevang Patel 
275e1649c31SDevang Patel /// getMachineBasicBlocks - Populate given set using machine basic blocks which
276e1649c31SDevang Patel /// have machine instructions that belong to lexical scope identified by
277e1649c31SDevang Patel /// DebugLoc.
2786211e4b9SEric Christopher void LexicalScopes::getMachineBasicBlocks(
279a9308c49SDuncan P. N. Exon Smith     const DILocation *DL, SmallPtrSetImpl<const MachineBasicBlock *> &MBBs) {
280*e018bbd8SWolfgang Pieb   assert(MF && "Method called on a uninitialized LexicalScopes object!");
281e1649c31SDevang Patel   MBBs.clear();
282*e018bbd8SWolfgang Pieb 
283e1649c31SDevang Patel   LexicalScope *Scope = getOrCreateLexicalScope(DL);
284e1649c31SDevang Patel   if (!Scope)
285e1649c31SDevang Patel     return;
286e1649c31SDevang Patel 
287db4374a2SDevang Patel   if (Scope == CurrentFnLexicalScope) {
28841b977dfSAlexey Samsonov     for (const auto &MBB : *MF)
28941b977dfSAlexey Samsonov       MBBs.insert(&MBB);
290db4374a2SDevang Patel     return;
291db4374a2SDevang Patel   }
292db4374a2SDevang Patel 
29380170e54SCraig Topper   SmallVectorImpl<InsnRange> &InsnRanges = Scope->getRanges();
29416b2ace0SAdrian Prantl   for (auto &R : InsnRanges)
295e1649c31SDevang Patel     MBBs.insert(R.first->getParent());
296e1649c31SDevang Patel }
297e1649c31SDevang Patel 
298e1649c31SDevang Patel /// dominates - Return true if DebugLoc's lexical scope dominates at least one
299e1649c31SDevang Patel /// machine instruction's lexical scope in a given machine basic block.
300a9308c49SDuncan P. N. Exon Smith bool LexicalScopes::dominates(const DILocation *DL, MachineBasicBlock *MBB) {
301*e018bbd8SWolfgang Pieb   assert(MF && "Unexpected uninitialized LexicalScopes object!");
302e1649c31SDevang Patel   LexicalScope *Scope = getOrCreateLexicalScope(DL);
303e1649c31SDevang Patel   if (!Scope)
304e1649c31SDevang Patel     return false;
305db4374a2SDevang Patel 
306db4374a2SDevang Patel   // Current function scope covers all basic blocks in the function.
307db4374a2SDevang Patel   if (Scope == CurrentFnLexicalScope && MBB->getParent() == MF)
308db4374a2SDevang Patel     return true;
309db4374a2SDevang Patel 
310e1649c31SDevang Patel   bool Result = false;
31116b2ace0SAdrian Prantl   for (auto &I : *MBB) {
31216b2ace0SAdrian Prantl     if (const DILocation *IDL = I.getDebugLoc())
313e1649c31SDevang Patel       if (LexicalScope *IScope = getOrCreateLexicalScope(IDL))
314e1649c31SDevang Patel         if (Scope->dominates(IScope))
315e1649c31SDevang Patel           return true;
316e1649c31SDevang Patel   }
317e1649c31SDevang Patel   return Result;
318e1649c31SDevang Patel }
319e1649c31SDevang Patel 
3208c209aa8SMatthias Braun #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
3218c209aa8SMatthias Braun LLVM_DUMP_METHOD void LexicalScope::dump(unsigned Indent) const {
322e1649c31SDevang Patel   raw_ostream &err = dbgs();
323e498b25bSManman Ren   err.indent(Indent);
324e1649c31SDevang Patel   err << "DFSIn: " << DFSIn << " DFSOut: " << DFSOut << "\n";
325e1649c31SDevang Patel   const MDNode *N = Desc;
326e498b25bSManman Ren   err.indent(Indent);
327e1649c31SDevang Patel   N->dump();
328e1649c31SDevang Patel   if (AbstractScope)
329e498b25bSManman Ren     err << std::string(Indent, ' ') << "Abstract Scope\n";
330e1649c31SDevang Patel 
331e1649c31SDevang Patel   if (!Children.empty())
332e498b25bSManman Ren     err << std::string(Indent + 2, ' ') << "Children ...\n";
333e1649c31SDevang Patel   for (unsigned i = 0, e = Children.size(); i != e; ++i)
334e1649c31SDevang Patel     if (Children[i] != this)
335e498b25bSManman Ren       Children[i]->dump(Indent + 2);
336e1649c31SDevang Patel }
3378c209aa8SMatthias Braun #endif
338