1*0b57cec5SDimitry Andric //===--- COFFModuleDefinition.cpp - Simple DEF parser ---------------------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // Windows-specific.
10*0b57cec5SDimitry Andric // A parser for the module-definition file (.def file).
11*0b57cec5SDimitry Andric //
12*0b57cec5SDimitry Andric // The format of module-definition files are described in this document:
13*0b57cec5SDimitry Andric // https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
14*0b57cec5SDimitry Andric //
15*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
16*0b57cec5SDimitry Andric
17*0b57cec5SDimitry Andric #include "llvm/Object/COFFModuleDefinition.h"
18*0b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
19*0b57cec5SDimitry Andric #include "llvm/ADT/StringSwitch.h"
20*0b57cec5SDimitry Andric #include "llvm/Object/COFF.h"
21*0b57cec5SDimitry Andric #include "llvm/Object/COFFImportFile.h"
22*0b57cec5SDimitry Andric #include "llvm/Object/Error.h"
23*0b57cec5SDimitry Andric #include "llvm/Support/Error.h"
24*0b57cec5SDimitry Andric #include "llvm/Support/Path.h"
25*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
26*0b57cec5SDimitry Andric
27*0b57cec5SDimitry Andric using namespace llvm::COFF;
28*0b57cec5SDimitry Andric using namespace llvm;
29*0b57cec5SDimitry Andric
30*0b57cec5SDimitry Andric namespace llvm {
31*0b57cec5SDimitry Andric namespace object {
32*0b57cec5SDimitry Andric
33*0b57cec5SDimitry Andric enum Kind {
34*0b57cec5SDimitry Andric Unknown,
35*0b57cec5SDimitry Andric Eof,
36*0b57cec5SDimitry Andric Identifier,
37*0b57cec5SDimitry Andric Comma,
38*0b57cec5SDimitry Andric Equal,
39*0b57cec5SDimitry Andric EqualEqual,
40*0b57cec5SDimitry Andric KwBase,
41*0b57cec5SDimitry Andric KwConstant,
42*0b57cec5SDimitry Andric KwData,
43*0b57cec5SDimitry Andric KwExports,
44*0b57cec5SDimitry Andric KwHeapsize,
45*0b57cec5SDimitry Andric KwLibrary,
46*0b57cec5SDimitry Andric KwName,
47*0b57cec5SDimitry Andric KwNoname,
48*0b57cec5SDimitry Andric KwPrivate,
49*0b57cec5SDimitry Andric KwStacksize,
50*0b57cec5SDimitry Andric KwVersion,
51*0b57cec5SDimitry Andric };
52*0b57cec5SDimitry Andric
53*0b57cec5SDimitry Andric struct Token {
Tokenllvm::object::Token54*0b57cec5SDimitry Andric explicit Token(Kind T = Unknown, StringRef S = "") : K(T), Value(S) {}
55*0b57cec5SDimitry Andric Kind K;
56*0b57cec5SDimitry Andric StringRef Value;
57*0b57cec5SDimitry Andric };
58*0b57cec5SDimitry Andric
isDecorated(StringRef Sym,bool MingwDef)59*0b57cec5SDimitry Andric static bool isDecorated(StringRef Sym, bool MingwDef) {
60*0b57cec5SDimitry Andric // In def files, the symbols can either be listed decorated or undecorated.
61*0b57cec5SDimitry Andric //
62*0b57cec5SDimitry Andric // - For cdecl symbols, only the undecorated form is allowed.
63*0b57cec5SDimitry Andric // - For fastcall and vectorcall symbols, both fully decorated or
64*0b57cec5SDimitry Andric // undecorated forms can be present.
65*0b57cec5SDimitry Andric // - For stdcall symbols in non-MinGW environments, the decorated form is
66*0b57cec5SDimitry Andric // fully decorated with leading underscore and trailing stack argument
67*0b57cec5SDimitry Andric // size - like "_Func@0".
68*0b57cec5SDimitry Andric // - In MinGW def files, a decorated stdcall symbol does not include the
69*0b57cec5SDimitry Andric // leading underscore though, like "Func@0".
70*0b57cec5SDimitry Andric
71*0b57cec5SDimitry Andric // This function controls whether a leading underscore should be added to
72*0b57cec5SDimitry Andric // the given symbol name or not. For MinGW, treat a stdcall symbol name such
73*0b57cec5SDimitry Andric // as "Func@0" as undecorated, i.e. a leading underscore must be added.
74*0b57cec5SDimitry Andric // For non-MinGW, look for '@' in the whole string and consider "_Func@0"
75*0b57cec5SDimitry Andric // as decorated, i.e. don't add any more leading underscores.
76*0b57cec5SDimitry Andric // We can't check for a leading underscore here, since function names
77*0b57cec5SDimitry Andric // themselves can start with an underscore, while a second one still needs
78*0b57cec5SDimitry Andric // to be added.
79*0b57cec5SDimitry Andric return Sym.startswith("@") || Sym.contains("@@") || Sym.startswith("?") ||
80*0b57cec5SDimitry Andric (!MingwDef && Sym.contains('@'));
81*0b57cec5SDimitry Andric }
82*0b57cec5SDimitry Andric
createError(const Twine & Err)83*0b57cec5SDimitry Andric static Error createError(const Twine &Err) {
84*0b57cec5SDimitry Andric return make_error<StringError>(StringRef(Err.str()),
85*0b57cec5SDimitry Andric object_error::parse_failed);
86*0b57cec5SDimitry Andric }
87*0b57cec5SDimitry Andric
88*0b57cec5SDimitry Andric class Lexer {
89*0b57cec5SDimitry Andric public:
Lexer(StringRef S)90*0b57cec5SDimitry Andric Lexer(StringRef S) : Buf(S) {}
91*0b57cec5SDimitry Andric
lex()92*0b57cec5SDimitry Andric Token lex() {
93*0b57cec5SDimitry Andric Buf = Buf.trim();
94*0b57cec5SDimitry Andric if (Buf.empty())
95*0b57cec5SDimitry Andric return Token(Eof);
96*0b57cec5SDimitry Andric
97*0b57cec5SDimitry Andric switch (Buf[0]) {
98*0b57cec5SDimitry Andric case '\0':
99*0b57cec5SDimitry Andric return Token(Eof);
100*0b57cec5SDimitry Andric case ';': {
101*0b57cec5SDimitry Andric size_t End = Buf.find('\n');
102*0b57cec5SDimitry Andric Buf = (End == Buf.npos) ? "" : Buf.drop_front(End);
103*0b57cec5SDimitry Andric return lex();
104*0b57cec5SDimitry Andric }
105*0b57cec5SDimitry Andric case '=':
106*0b57cec5SDimitry Andric Buf = Buf.drop_front();
107*0b57cec5SDimitry Andric if (Buf.startswith("=")) {
108*0b57cec5SDimitry Andric Buf = Buf.drop_front();
109*0b57cec5SDimitry Andric return Token(EqualEqual, "==");
110*0b57cec5SDimitry Andric }
111*0b57cec5SDimitry Andric return Token(Equal, "=");
112*0b57cec5SDimitry Andric case ',':
113*0b57cec5SDimitry Andric Buf = Buf.drop_front();
114*0b57cec5SDimitry Andric return Token(Comma, ",");
115*0b57cec5SDimitry Andric case '"': {
116*0b57cec5SDimitry Andric StringRef S;
117*0b57cec5SDimitry Andric std::tie(S, Buf) = Buf.substr(1).split('"');
118*0b57cec5SDimitry Andric return Token(Identifier, S);
119*0b57cec5SDimitry Andric }
120*0b57cec5SDimitry Andric default: {
121*0b57cec5SDimitry Andric size_t End = Buf.find_first_of("=,;\r\n \t\v");
122*0b57cec5SDimitry Andric StringRef Word = Buf.substr(0, End);
123*0b57cec5SDimitry Andric Kind K = llvm::StringSwitch<Kind>(Word)
124*0b57cec5SDimitry Andric .Case("BASE", KwBase)
125*0b57cec5SDimitry Andric .Case("CONSTANT", KwConstant)
126*0b57cec5SDimitry Andric .Case("DATA", KwData)
127*0b57cec5SDimitry Andric .Case("EXPORTS", KwExports)
128*0b57cec5SDimitry Andric .Case("HEAPSIZE", KwHeapsize)
129*0b57cec5SDimitry Andric .Case("LIBRARY", KwLibrary)
130*0b57cec5SDimitry Andric .Case("NAME", KwName)
131*0b57cec5SDimitry Andric .Case("NONAME", KwNoname)
132*0b57cec5SDimitry Andric .Case("PRIVATE", KwPrivate)
133*0b57cec5SDimitry Andric .Case("STACKSIZE", KwStacksize)
134*0b57cec5SDimitry Andric .Case("VERSION", KwVersion)
135*0b57cec5SDimitry Andric .Default(Identifier);
136*0b57cec5SDimitry Andric Buf = (End == Buf.npos) ? "" : Buf.drop_front(End);
137*0b57cec5SDimitry Andric return Token(K, Word);
138*0b57cec5SDimitry Andric }
139*0b57cec5SDimitry Andric }
140*0b57cec5SDimitry Andric }
141*0b57cec5SDimitry Andric
142*0b57cec5SDimitry Andric private:
143*0b57cec5SDimitry Andric StringRef Buf;
144*0b57cec5SDimitry Andric };
145*0b57cec5SDimitry Andric
146*0b57cec5SDimitry Andric class Parser {
147*0b57cec5SDimitry Andric public:
Parser(StringRef S,MachineTypes M,bool B)148*0b57cec5SDimitry Andric explicit Parser(StringRef S, MachineTypes M, bool B)
149*0b57cec5SDimitry Andric : Lex(S), Machine(M), MingwDef(B) {}
150*0b57cec5SDimitry Andric
parse()151*0b57cec5SDimitry Andric Expected<COFFModuleDefinition> parse() {
152*0b57cec5SDimitry Andric do {
153*0b57cec5SDimitry Andric if (Error Err = parseOne())
154*0b57cec5SDimitry Andric return std::move(Err);
155*0b57cec5SDimitry Andric } while (Tok.K != Eof);
156*0b57cec5SDimitry Andric return Info;
157*0b57cec5SDimitry Andric }
158*0b57cec5SDimitry Andric
159*0b57cec5SDimitry Andric private:
read()160*0b57cec5SDimitry Andric void read() {
161*0b57cec5SDimitry Andric if (Stack.empty()) {
162*0b57cec5SDimitry Andric Tok = Lex.lex();
163*0b57cec5SDimitry Andric return;
164*0b57cec5SDimitry Andric }
165*0b57cec5SDimitry Andric Tok = Stack.back();
166*0b57cec5SDimitry Andric Stack.pop_back();
167*0b57cec5SDimitry Andric }
168*0b57cec5SDimitry Andric
readAsInt(uint64_t * I)169*0b57cec5SDimitry Andric Error readAsInt(uint64_t *I) {
170*0b57cec5SDimitry Andric read();
171*0b57cec5SDimitry Andric if (Tok.K != Identifier || Tok.Value.getAsInteger(10, *I))
172*0b57cec5SDimitry Andric return createError("integer expected");
173*0b57cec5SDimitry Andric return Error::success();
174*0b57cec5SDimitry Andric }
175*0b57cec5SDimitry Andric
expect(Kind Expected,StringRef Msg)176*0b57cec5SDimitry Andric Error expect(Kind Expected, StringRef Msg) {
177*0b57cec5SDimitry Andric read();
178*0b57cec5SDimitry Andric if (Tok.K != Expected)
179*0b57cec5SDimitry Andric return createError(Msg);
180*0b57cec5SDimitry Andric return Error::success();
181*0b57cec5SDimitry Andric }
182*0b57cec5SDimitry Andric
unget()183*0b57cec5SDimitry Andric void unget() { Stack.push_back(Tok); }
184*0b57cec5SDimitry Andric
parseOne()185*0b57cec5SDimitry Andric Error parseOne() {
186*0b57cec5SDimitry Andric read();
187*0b57cec5SDimitry Andric switch (Tok.K) {
188*0b57cec5SDimitry Andric case Eof:
189*0b57cec5SDimitry Andric return Error::success();
190*0b57cec5SDimitry Andric case KwExports:
191*0b57cec5SDimitry Andric for (;;) {
192*0b57cec5SDimitry Andric read();
193*0b57cec5SDimitry Andric if (Tok.K != Identifier) {
194*0b57cec5SDimitry Andric unget();
195*0b57cec5SDimitry Andric return Error::success();
196*0b57cec5SDimitry Andric }
197*0b57cec5SDimitry Andric if (Error Err = parseExport())
198*0b57cec5SDimitry Andric return Err;
199*0b57cec5SDimitry Andric }
200*0b57cec5SDimitry Andric case KwHeapsize:
201*0b57cec5SDimitry Andric return parseNumbers(&Info.HeapReserve, &Info.HeapCommit);
202*0b57cec5SDimitry Andric case KwStacksize:
203*0b57cec5SDimitry Andric return parseNumbers(&Info.StackReserve, &Info.StackCommit);
204*0b57cec5SDimitry Andric case KwLibrary:
205*0b57cec5SDimitry Andric case KwName: {
206*0b57cec5SDimitry Andric bool IsDll = Tok.K == KwLibrary; // Check before parseName.
207*0b57cec5SDimitry Andric std::string Name;
208*0b57cec5SDimitry Andric if (Error Err = parseName(&Name, &Info.ImageBase))
209*0b57cec5SDimitry Andric return Err;
210*0b57cec5SDimitry Andric
211*0b57cec5SDimitry Andric Info.ImportName = Name;
212*0b57cec5SDimitry Andric
213*0b57cec5SDimitry Andric // Set the output file, but don't override /out if it was already passed.
214*0b57cec5SDimitry Andric if (Info.OutputFile.empty()) {
215*0b57cec5SDimitry Andric Info.OutputFile = Name;
216*0b57cec5SDimitry Andric // Append the appropriate file extension if not already present.
217*0b57cec5SDimitry Andric if (!sys::path::has_extension(Name))
218*0b57cec5SDimitry Andric Info.OutputFile += IsDll ? ".dll" : ".exe";
219*0b57cec5SDimitry Andric }
220*0b57cec5SDimitry Andric
221*0b57cec5SDimitry Andric return Error::success();
222*0b57cec5SDimitry Andric }
223*0b57cec5SDimitry Andric case KwVersion:
224*0b57cec5SDimitry Andric return parseVersion(&Info.MajorImageVersion, &Info.MinorImageVersion);
225*0b57cec5SDimitry Andric default:
226*0b57cec5SDimitry Andric return createError("unknown directive: " + Tok.Value);
227*0b57cec5SDimitry Andric }
228*0b57cec5SDimitry Andric }
229*0b57cec5SDimitry Andric
parseExport()230*0b57cec5SDimitry Andric Error parseExport() {
231*0b57cec5SDimitry Andric COFFShortExport E;
232*0b57cec5SDimitry Andric E.Name = std::string(Tok.Value);
233*0b57cec5SDimitry Andric read();
234*0b57cec5SDimitry Andric if (Tok.K == Equal) {
235*0b57cec5SDimitry Andric read();
236*0b57cec5SDimitry Andric if (Tok.K != Identifier)
237*0b57cec5SDimitry Andric return createError("identifier expected, but got " + Tok.Value);
238*0b57cec5SDimitry Andric E.ExtName = E.Name;
239*0b57cec5SDimitry Andric E.Name = std::string(Tok.Value);
240*0b57cec5SDimitry Andric } else {
241*0b57cec5SDimitry Andric unget();
242*0b57cec5SDimitry Andric }
243*0b57cec5SDimitry Andric
244*0b57cec5SDimitry Andric if (Machine == IMAGE_FILE_MACHINE_I386) {
245*0b57cec5SDimitry Andric if (!isDecorated(E.Name, MingwDef))
246*0b57cec5SDimitry Andric E.Name = (std::string("_").append(E.Name));
247*0b57cec5SDimitry Andric if (!E.ExtName.empty() && !isDecorated(E.ExtName, MingwDef))
248*0b57cec5SDimitry Andric E.ExtName = (std::string("_").append(E.ExtName));
249*0b57cec5SDimitry Andric }
250*0b57cec5SDimitry Andric
251*0b57cec5SDimitry Andric for (;;) {
252*0b57cec5SDimitry Andric read();
253*0b57cec5SDimitry Andric if (Tok.K == Identifier && Tok.Value[0] == '@') {
254*0b57cec5SDimitry Andric if (Tok.Value == "@") {
255*0b57cec5SDimitry Andric // "foo @ 10"
256*0b57cec5SDimitry Andric read();
257*0b57cec5SDimitry Andric Tok.Value.getAsInteger(10, E.Ordinal);
258*0b57cec5SDimitry Andric } else if (Tok.Value.drop_front().getAsInteger(10, E.Ordinal)) {
259*0b57cec5SDimitry Andric // "foo \n @bar" - Not an ordinal modifier at all, but the next
260*0b57cec5SDimitry Andric // export (fastcall decorated) - complete the current one.
261*0b57cec5SDimitry Andric unget();
262*0b57cec5SDimitry Andric Info.Exports.push_back(E);
263*0b57cec5SDimitry Andric return Error::success();
264*0b57cec5SDimitry Andric }
265*0b57cec5SDimitry Andric // "foo @10"
266*0b57cec5SDimitry Andric read();
267*0b57cec5SDimitry Andric if (Tok.K == KwNoname) {
268*0b57cec5SDimitry Andric E.Noname = true;
269*0b57cec5SDimitry Andric } else {
270*0b57cec5SDimitry Andric unget();
271*0b57cec5SDimitry Andric }
272*0b57cec5SDimitry Andric continue;
273*0b57cec5SDimitry Andric }
274*0b57cec5SDimitry Andric if (Tok.K == KwData) {
275*0b57cec5SDimitry Andric E.Data = true;
276*0b57cec5SDimitry Andric continue;
277*0b57cec5SDimitry Andric }
278*0b57cec5SDimitry Andric if (Tok.K == KwConstant) {
279*0b57cec5SDimitry Andric E.Constant = true;
280*0b57cec5SDimitry Andric continue;
281*0b57cec5SDimitry Andric }
282*0b57cec5SDimitry Andric if (Tok.K == KwPrivate) {
283*0b57cec5SDimitry Andric E.Private = true;
284*0b57cec5SDimitry Andric continue;
285*0b57cec5SDimitry Andric }
286*0b57cec5SDimitry Andric if (Tok.K == EqualEqual) {
287*0b57cec5SDimitry Andric read();
288*0b57cec5SDimitry Andric E.AliasTarget = std::string(Tok.Value);
289*0b57cec5SDimitry Andric if (Machine == IMAGE_FILE_MACHINE_I386 && !isDecorated(E.AliasTarget, MingwDef))
290*0b57cec5SDimitry Andric E.AliasTarget = std::string("_").append(E.AliasTarget);
291*0b57cec5SDimitry Andric continue;
292*0b57cec5SDimitry Andric }
293*0b57cec5SDimitry Andric unget();
294*0b57cec5SDimitry Andric Info.Exports.push_back(E);
295*0b57cec5SDimitry Andric return Error::success();
296*0b57cec5SDimitry Andric }
297*0b57cec5SDimitry Andric }
298*0b57cec5SDimitry Andric
299*0b57cec5SDimitry Andric // HEAPSIZE/STACKSIZE reserve[,commit]
parseNumbers(uint64_t * Reserve,uint64_t * Commit)300*0b57cec5SDimitry Andric Error parseNumbers(uint64_t *Reserve, uint64_t *Commit) {
301*0b57cec5SDimitry Andric if (Error Err = readAsInt(Reserve))
302*0b57cec5SDimitry Andric return Err;
303*0b57cec5SDimitry Andric read();
304*0b57cec5SDimitry Andric if (Tok.K != Comma) {
305*0b57cec5SDimitry Andric unget();
306*0b57cec5SDimitry Andric Commit = nullptr;
307*0b57cec5SDimitry Andric return Error::success();
308*0b57cec5SDimitry Andric }
309*0b57cec5SDimitry Andric if (Error Err = readAsInt(Commit))
310*0b57cec5SDimitry Andric return Err;
311*0b57cec5SDimitry Andric return Error::success();
312*0b57cec5SDimitry Andric }
313*0b57cec5SDimitry Andric
314*0b57cec5SDimitry Andric // NAME outputPath [BASE=address]
parseName(std::string * Out,uint64_t * Baseaddr)315*0b57cec5SDimitry Andric Error parseName(std::string *Out, uint64_t *Baseaddr) {
316*0b57cec5SDimitry Andric read();
317*0b57cec5SDimitry Andric if (Tok.K == Identifier) {
318*0b57cec5SDimitry Andric *Out = std::string(Tok.Value);
319*0b57cec5SDimitry Andric } else {
320*0b57cec5SDimitry Andric *Out = "";
321*0b57cec5SDimitry Andric unget();
322*0b57cec5SDimitry Andric return Error::success();
323*0b57cec5SDimitry Andric }
324*0b57cec5SDimitry Andric read();
325*0b57cec5SDimitry Andric if (Tok.K == KwBase) {
326*0b57cec5SDimitry Andric if (Error Err = expect(Equal, "'=' expected"))
327*0b57cec5SDimitry Andric return Err;
328*0b57cec5SDimitry Andric if (Error Err = readAsInt(Baseaddr))
329*0b57cec5SDimitry Andric return Err;
330*0b57cec5SDimitry Andric } else {
331*0b57cec5SDimitry Andric unget();
332*0b57cec5SDimitry Andric *Baseaddr = 0;
333*0b57cec5SDimitry Andric }
334*0b57cec5SDimitry Andric return Error::success();
335*0b57cec5SDimitry Andric }
336*0b57cec5SDimitry Andric
337*0b57cec5SDimitry Andric // VERSION major[.minor]
parseVersion(uint32_t * Major,uint32_t * Minor)338*0b57cec5SDimitry Andric Error parseVersion(uint32_t *Major, uint32_t *Minor) {
339*0b57cec5SDimitry Andric read();
340*0b57cec5SDimitry Andric if (Tok.K != Identifier)
341*0b57cec5SDimitry Andric return createError("identifier expected, but got " + Tok.Value);
342*0b57cec5SDimitry Andric StringRef V1, V2;
343*0b57cec5SDimitry Andric std::tie(V1, V2) = Tok.Value.split('.');
344*0b57cec5SDimitry Andric if (V1.getAsInteger(10, *Major))
345*0b57cec5SDimitry Andric return createError("integer expected, but got " + Tok.Value);
346*0b57cec5SDimitry Andric if (V2.empty())
347*0b57cec5SDimitry Andric *Minor = 0;
348*0b57cec5SDimitry Andric else if (V2.getAsInteger(10, *Minor))
349*0b57cec5SDimitry Andric return createError("integer expected, but got " + Tok.Value);
350*0b57cec5SDimitry Andric return Error::success();
351*0b57cec5SDimitry Andric }
352*0b57cec5SDimitry Andric
353*0b57cec5SDimitry Andric Lexer Lex;
354*0b57cec5SDimitry Andric Token Tok;
355*0b57cec5SDimitry Andric std::vector<Token> Stack;
356*0b57cec5SDimitry Andric MachineTypes Machine;
357*0b57cec5SDimitry Andric COFFModuleDefinition Info;
358*0b57cec5SDimitry Andric bool MingwDef;
359*0b57cec5SDimitry Andric };
360*0b57cec5SDimitry Andric
parseCOFFModuleDefinition(MemoryBufferRef MB,MachineTypes Machine,bool MingwDef)361*0b57cec5SDimitry Andric Expected<COFFModuleDefinition> parseCOFFModuleDefinition(MemoryBufferRef MB,
362*0b57cec5SDimitry Andric MachineTypes Machine,
363*0b57cec5SDimitry Andric bool MingwDef) {
364*0b57cec5SDimitry Andric return Parser(MB.getBuffer(), Machine, MingwDef).parse();
365*0b57cec5SDimitry Andric }
366*0b57cec5SDimitry Andric
367*0b57cec5SDimitry Andric } // namespace object
368*0b57cec5SDimitry Andric } // namespace llvm
369