use the clang driver to obtain command line arguments for the clang frontend
[pet.git] / pet.cc
blobf1d116d813b28d71f933967cf9014667521fbdbc
1 /*
2 * Copyright 2011 Leiden University. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials provided
14 * with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY LEIDEN UNIVERSITY ''AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LEIDEN UNIVERSITY OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * The views and conclusions contained in the software and documentation
29 * are those of the authors and should not be interpreted as
30 * representing official policies, either expressed or implied, of
31 * Leiden University.
32 */
34 #include <stdlib.h>
35 #include <map>
36 #include <iostream>
37 #include <llvm/Support/raw_ostream.h>
38 #include <llvm/Support/ManagedStatic.h>
39 #include <llvm/Support/Host.h>
40 #include <clang/Basic/Version.h>
41 #include <clang/Basic/FileSystemOptions.h>
42 #include <clang/Basic/FileManager.h>
43 #include <clang/Basic/TargetOptions.h>
44 #include <clang/Basic/TargetInfo.h>
45 #include <clang/Driver/Compilation.h>
46 #include <clang/Driver/Driver.h>
47 #include <clang/Driver/Tool.h>
48 #include <clang/Frontend/CompilerInstance.h>
49 #include <clang/Frontend/CompilerInvocation.h>
50 #include <clang/Frontend/DiagnosticOptions.h>
51 #include <clang/Frontend/TextDiagnosticPrinter.h>
52 #include <clang/Frontend/HeaderSearchOptions.h>
53 #include <clang/Frontend/LangStandard.h>
54 #include <clang/Frontend/PreprocessorOptions.h>
55 #include <clang/Frontend/FrontendOptions.h>
56 #include <clang/Frontend/Utils.h>
57 #include <clang/Lex/HeaderSearch.h>
58 #include <clang/Lex/Preprocessor.h>
59 #include <clang/Lex/Pragma.h>
60 #include <clang/AST/ASTContext.h>
61 #include <clang/AST/ASTConsumer.h>
62 #include <clang/Sema/Sema.h>
63 #include <clang/Sema/SemaDiagnostic.h>
64 #include <clang/Parse/Parser.h>
65 #include <clang/Parse/ParseAST.h>
67 #include <isl/ctx.h>
68 #include <isl/constraint.h>
70 #include "scan.h"
72 #include "config.h"
74 #define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array))
76 using namespace std;
77 using namespace clang;
78 using namespace clang::driver;
80 /* Called if we found something we didn't expect in one of the pragmas.
81 * We'll provide more informative warnings later.
83 static void unsupported(Preprocessor &PP, SourceLocation loc)
85 DiagnosticsEngine &diag = PP.getDiagnostics();
86 unsigned id = diag.getCustomDiagID(DiagnosticsEngine::Warning,
87 "unsupported");
88 DiagnosticBuilder B = diag.Report(loc, id);
91 static int get_int(const char *s)
93 return s[0] == '"' ? atoi(s + 1) : atoi(s);
96 static ValueDecl *get_value_decl(Sema &sema, Token &token)
98 IdentifierInfo *name;
99 Decl *decl;
101 if (token.isNot(tok::identifier))
102 return NULL;
104 name = token.getIdentifierInfo();
105 decl = sema.LookupSingleName(sema.TUScope, name,
106 token.getLocation(), Sema::LookupOrdinaryName);
107 return decl ? cast_or_null<ValueDecl>(decl) : NULL;
110 /* Handle pragmas of the form
112 * #pragma value_bounds identifier lower_bound upper_bound
114 * For each such pragma, add a mapping from the ValueDecl corresponding
115 * to "identifier" to a set { [i] : lower_bound <= i <= upper_bound }
116 * to the map value_bounds.
118 struct PragmaValueBoundsHandler : public PragmaHandler {
119 Sema &sema;
120 isl_ctx *ctx;
121 map<ValueDecl *, isl_set *> &value_bounds;
123 PragmaValueBoundsHandler(isl_ctx *ctx, Sema &sema,
124 map<ValueDecl *, isl_set *> &value_bounds) :
125 PragmaHandler("value_bounds"), ctx(ctx), sema(sema),
126 value_bounds(value_bounds) {}
128 virtual void HandlePragma(Preprocessor &PP,
129 PragmaIntroducerKind Introducer,
130 Token &ScopTok) {
131 isl_space *dim;
132 isl_set *set;
133 ValueDecl *vd;
134 Token token;
135 int lb;
136 int ub;
138 PP.Lex(token);
139 vd = get_value_decl(sema, token);
140 if (!vd) {
141 unsupported(PP, token.getLocation());
142 return;
145 PP.Lex(token);
146 if (!token.isLiteral()) {
147 unsupported(PP, token.getLocation());
148 return;
151 lb = get_int(token.getLiteralData());
153 PP.Lex(token);
154 if (!token.isLiteral()) {
155 unsupported(PP, token.getLocation());
156 return;
159 ub = get_int(token.getLiteralData());
161 dim = isl_space_set_alloc(ctx, 0, 1);
162 set = isl_set_universe(dim);
163 set = isl_set_lower_bound_si(set, isl_dim_set, 0, lb);
164 set = isl_set_upper_bound_si(set, isl_dim_set, 0, ub);
166 value_bounds[vd] = set;
170 /* Given a variable declaration, check if it has an integer initializer
171 * and if so, add a parameter corresponding to the variable to "value"
172 * with its value fixed to the integer initializer and return the result.
174 static __isl_give isl_set *extract_initialization(__isl_take isl_set *value,
175 ValueDecl *decl)
177 VarDecl *vd;
178 Expr *expr;
179 IntegerLiteral *il;
180 isl_int v;
181 isl_ctx *ctx;
182 isl_id *id;
183 isl_space *space;
184 isl_set *set;
186 vd = cast<VarDecl>(decl);
187 if (!vd)
188 return value;
189 if (!vd->getType()->isIntegerType())
190 return value;
191 expr = vd->getInit();
192 if (!expr)
193 return value;
194 il = cast<IntegerLiteral>(expr);
195 if (!il)
196 return value;
198 ctx = isl_set_get_ctx(value);
199 id = isl_id_alloc(ctx, vd->getName().str().c_str(), vd);
200 space = isl_space_params_alloc(ctx, 1);
201 space = isl_space_set_dim_id(space, isl_dim_param, 0, id);
202 set = isl_set_universe(space);
204 isl_int_init(v);
205 PetScan::extract_int(il, &v);
206 set = isl_set_fix(set, isl_dim_param, 0, v);
207 isl_int_clear(v);
209 return isl_set_intersect(value, set);
212 /* Handle pragmas of the form
214 * #pragma parameter identifier lower_bound
215 * and
216 * #pragma parameter identifier lower_bound upper_bound
218 * For each such pragma, intersect the context with the set
219 * [identifier] -> { [] : lower_bound <= identifier <= upper_bound }
221 struct PragmaParameterHandler : public PragmaHandler {
222 Sema &sema;
223 isl_set *&context;
224 isl_set *&context_value;
226 PragmaParameterHandler(Sema &sema, isl_set *&context,
227 isl_set *&context_value) :
228 PragmaHandler("parameter"), sema(sema), context(context),
229 context_value(context_value) {}
231 virtual void HandlePragma(Preprocessor &PP,
232 PragmaIntroducerKind Introducer,
233 Token &ScopTok) {
234 isl_id *id;
235 isl_ctx *ctx = isl_set_get_ctx(context);
236 isl_space *dim;
237 isl_set *set;
238 ValueDecl *vd;
239 Token token;
240 int lb;
241 int ub;
242 bool has_ub = false;
244 PP.Lex(token);
245 vd = get_value_decl(sema, token);
246 if (!vd) {
247 unsupported(PP, token.getLocation());
248 return;
251 PP.Lex(token);
252 if (!token.isLiteral()) {
253 unsupported(PP, token.getLocation());
254 return;
257 lb = get_int(token.getLiteralData());
259 PP.Lex(token);
260 if (token.isLiteral()) {
261 has_ub = true;
262 ub = get_int(token.getLiteralData());
263 } else if (token.isNot(tok::eod)) {
264 unsupported(PP, token.getLocation());
265 return;
268 id = isl_id_alloc(ctx, vd->getName().str().c_str(), vd);
269 dim = isl_space_params_alloc(ctx, 1);
270 dim = isl_space_set_dim_id(dim, isl_dim_param, 0, id);
272 set = isl_set_universe(dim);
274 set = isl_set_lower_bound_si(set, isl_dim_param, 0, lb);
275 if (has_ub)
276 set = isl_set_upper_bound_si(set, isl_dim_param, 0, ub);
278 context = isl_set_intersect(context, set);
280 context_value = extract_initialization(context_value, vd);
284 /* Handle pragmas of the form
286 * #pragma scop
288 * In particular, store the current location in loc.start.
290 struct PragmaScopHandler : public PragmaHandler {
291 ScopLoc &loc;
293 PragmaScopHandler(ScopLoc &loc) : PragmaHandler("scop"), loc(loc) {}
295 virtual void HandlePragma(Preprocessor &PP,
296 PragmaIntroducerKind Introducer,
297 Token &ScopTok) {
298 SourceManager &SM = PP.getSourceManager();
299 loc.start = SM.getFileOffset(ScopTok.getLocation());
303 /* Handle pragmas of the form
305 * #pragma endscop
307 * In particular, store the current location in loc.end.
309 struct PragmaEndScopHandler : public PragmaHandler {
310 ScopLoc &loc;
312 PragmaEndScopHandler(ScopLoc &loc) :
313 PragmaHandler("endscop"), loc(loc) {}
315 virtual void HandlePragma(Preprocessor &PP,
316 PragmaIntroducerKind Introducer,
317 Token &EndScopTok) {
318 SourceManager &SM = PP.getSourceManager();
319 loc.end = SM.getFileOffset(EndScopTok.getLocation());
323 /* Handle pragmas of the form
325 * #pragma live-out identifier, identifier, ...
327 * Each identifier on the line is stored in live_out.
329 struct PragmaLiveOutHandler : public PragmaHandler {
330 Sema &sema;
331 set<ValueDecl *> &live_out;
333 PragmaLiveOutHandler(Sema &sema, set<ValueDecl *> &live_out) :
334 PragmaHandler("live"), sema(sema), live_out(live_out) {}
336 virtual void HandlePragma(Preprocessor &PP,
337 PragmaIntroducerKind Introducer,
338 Token &ScopTok) {
339 Token token;
341 PP.Lex(token);
342 if (token.isNot(tok::minus))
343 return;
344 PP.Lex(token);
345 if (token.isNot(tok::identifier) ||
346 !token.getIdentifierInfo()->isStr("out"))
347 return;
349 PP.Lex(token);
350 while (token.isNot(tok::eod)) {
351 ValueDecl *vd;
353 vd = get_value_decl(sema, token);
354 if (!vd) {
355 unsupported(PP, token.getLocation());
356 return;
358 live_out.insert(vd);
359 PP.Lex(token);
360 if (token.is(tok::comma))
361 PP.Lex(token);
366 /* Extract a pet_scop from the appropriate function.
367 * If "function" is not NULL, then we only extract a pet_scop if the
368 * name of the function matches.
369 * If "autodetect" is false, then we only extract if we have seen
370 * scop and endscop pragmas and if these are situated inside the function
371 * body.
373 struct PetASTConsumer : public ASTConsumer {
374 Preprocessor &PP;
375 ASTContext &ast_context;
376 ScopLoc &loc;
377 const char *function;
378 bool autodetect;
379 isl_ctx *ctx;
380 struct pet_scop *scop;
382 PetASTConsumer(isl_ctx *ctx, Preprocessor &PP, ASTContext &ast_context,
383 ScopLoc &loc, const char *function, bool autodetect) :
384 ctx(ctx), PP(PP), ast_context(ast_context), loc(loc),
385 scop(NULL), function(function), autodetect(autodetect) { }
387 virtual void HandleTopLevelDecl(DeclGroupRef dg) {
388 DeclGroupRef::iterator it;
390 if (scop)
391 return;
392 for (it = dg.begin(); it != dg.end(); ++it) {
393 FunctionDecl *fd = dyn_cast<clang::FunctionDecl>(*it);
394 if (!fd)
395 continue;
396 if (!fd->hasBody())
397 continue;
398 if (function &&
399 fd->getNameInfo().getAsString() != function)
400 continue;
401 if (autodetect) {
402 PetScan ps(ctx, PP, ast_context, loc, 1);
403 scop = ps.scan(fd);
404 if (scop)
405 break;
406 else
407 continue;
409 if (!loc.end)
410 continue;
411 SourceManager &SM = PP.getSourceManager();
412 if (SM.getFileOffset(fd->getLocStart()) > loc.end)
413 continue;
414 if (SM.getFileOffset(fd->getLocEnd()) < loc.start)
415 continue;
416 PetScan ps(ctx, PP, ast_context, loc, 0);
417 scop = ps.scan(fd);
418 break;
423 static const char *ResourceDir = CLANG_PREFIX"/lib/clang/"CLANG_VERSION_STRING;
425 static const char *implicit_functions[] = {
426 "min", "max", "ceild", "floord"
429 static bool is_implicit(const IdentifierInfo *ident)
431 const char *name = ident->getNameStart();
432 for (int i = 0; i < ARRAY_SIZE(implicit_functions); ++i)
433 if (!strcmp(name, implicit_functions[i]))
434 return true;
435 return false;
438 /* Ignore implicit function declaration warnings on
439 * "min", "max", "ceild" and "floord" as we detect and handle these
440 * in PetScan.
442 * The cloned field keeps track of whether the clone method
443 * has ever been called. Newer clangs (by default) clone
444 * the DiagnosticConsumer passed to createDiagnostics and
445 * then take ownership of the clone, which means that
446 * the original has to be deleted by the calling code.
448 struct MyDiagnosticPrinter : public TextDiagnosticPrinter {
449 const DiagnosticOptions *DiagOpts;
450 static bool cloned;
451 MyDiagnosticPrinter(const DiagnosticOptions &DO) :
452 DiagOpts(&DO), TextDiagnosticPrinter(llvm::errs(), DO) {}
453 virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
454 cloned = true;
455 return new MyDiagnosticPrinter(*DiagOpts);
457 virtual void HandleDiagnostic(DiagnosticsEngine::Level level,
458 const DiagnosticInfo &info) {
459 if (info.getID() == diag::ext_implicit_function_decl &&
460 info.getNumArgs() == 1 &&
461 info.getArgKind(0) == DiagnosticsEngine::ak_identifierinfo &&
462 is_implicit(info.getArgIdentifier(0)))
463 /* ignore warning */;
464 else
465 TextDiagnosticPrinter::HandleDiagnostic(level, info);
469 bool MyDiagnosticPrinter::cloned = false;
471 static void update_arrays(struct pet_scop *scop,
472 map<ValueDecl *, isl_set *> &value_bounds,
473 set<ValueDecl *> &live_out)
475 map<ValueDecl *, isl_set *>::iterator vb_it;
476 set<ValueDecl *>::iterator lo_it;
478 if (!scop)
479 return;
481 for (int i = 0; i < scop->n_array; ++i) {
482 isl_id *id;
483 ValueDecl *decl;
484 pet_array *array = scop->arrays[i];
486 id = isl_set_get_tuple_id(array->extent);
487 decl = (ValueDecl *)isl_id_get_user(id);
488 isl_id_free(id);
490 vb_it = value_bounds.find(decl);
491 if (vb_it != value_bounds.end())
492 array->value_bounds = isl_set_copy(vb_it->second);
494 lo_it = live_out.find(decl);
495 if (lo_it != live_out.end())
496 array->live_out = 1;
500 #ifdef USE_ARRAYREF
502 #ifdef HAVE_CXXISPRODUCTION
503 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
505 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
506 "", false, false, Diags);
508 #else
509 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
511 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
512 "", false, Diags);
514 #endif
516 /* Create a CompilerInvocation object that stores the command line
517 * arguments constructed by the driver.
518 * The arguments are mainly useful for setting up the system include
519 * paths on newer clangs and on some platforms.
521 static CompilerInvocation *construct_invocation(const char *filename,
522 DiagnosticsEngine &Diags)
524 const char *binary = CLANG_PREFIX"/bin/clang";
525 const llvm::OwningPtr<Driver> driver(construct_driver(binary, Diags));
526 std::vector<const char *> Argv;
527 Argv.push_back(binary);
528 Argv.push_back(filename);
529 const llvm::OwningPtr<Compilation> compilation(
530 driver->BuildCompilation(ArrayRef<const char *>(Argv)));
531 JobList &Jobs = compilation->getJobs();
533 Command *cmd = cast<Command>(*Jobs.begin());
534 if (strcmp(cmd->getCreator().getName(), "clang"))
535 return NULL;
537 const ArgStringList *args = &cmd->getArguments();
539 CompilerInvocation *invocation = new CompilerInvocation;
540 CompilerInvocation::CreateFromArgs(*invocation, args->data() + 1,
541 args->data() + args->size(),
542 Diags);
543 return invocation;
546 #else
548 static CompilerInvocation *construct_invocation(const char *filename,
549 DiagnosticsEngine &Diags)
551 return NULL;
554 #endif
556 /* Extract a pet_scop from the C source file called "filename".
557 * If "function" is not NULL, extract the pet_scop from the function
558 * with that name.
559 * If "autodetect" is set, extract any pet_scop we can find.
560 * Otherwise, extract the pet_scop from the region delimited
561 * by "scop" and "endscop" pragmas.
563 * We first set up the clang parser and then try to extract the
564 * pet_scop from the appropriate function in PetASTConsumer.
565 * If we have found a pet_scop, we add the context and value_bounds
566 * constraints specified through pragmas.
568 struct pet_scop *pet_scop_extract_from_C_source(isl_ctx *ctx,
569 const char *filename, const char *function, int autodetect)
571 isl_space *dim;
572 isl_set *context;
573 isl_set *context_value;
574 set<ValueDecl *> live_out;
575 map<ValueDecl *, isl_set *> value_bounds;
576 map<ValueDecl *, isl_set *>::iterator vb_it;
578 CompilerInstance *Clang = new CompilerInstance();
579 DiagnosticOptions DO;
580 MyDiagnosticPrinter *printer = new MyDiagnosticPrinter(DO);
581 Clang->createDiagnostics(0, NULL, printer);
582 if (printer->cloned)
583 delete printer;
584 DiagnosticsEngine &Diags = Clang->getDiagnostics();
585 Diags.setSuppressSystemWarnings(true);
586 CompilerInvocation *invocation = construct_invocation(filename, Diags);
587 if (invocation)
588 Clang->setInvocation(invocation);
589 Clang->createFileManager();
590 Clang->createSourceManager(Clang->getFileManager());
591 TargetOptions TO;
592 TO.Triple = llvm::sys::getDefaultTargetTriple();
593 TargetInfo *target = TargetInfo::CreateTargetInfo(Diags, TO);
594 Clang->setTarget(target);
595 CompilerInvocation::setLangDefaults(Clang->getLangOpts(), IK_C,
596 LangStandard::lang_unspecified);
597 Clang->getHeaderSearchOpts().ResourceDir = ResourceDir;
598 Clang->createPreprocessor();
599 Preprocessor &PP = Clang->getPreprocessor();
601 ScopLoc loc;
603 const FileEntry *file = Clang->getFileManager().getFile(filename);
604 if (!file)
605 isl_die(ctx, isl_error_unknown, "unable to open file",
606 return NULL);
607 Clang->getSourceManager().createMainFileID(file);
609 Clang->createASTContext();
610 PetASTConsumer consumer(ctx, PP, Clang->getASTContext(),
611 loc, function, autodetect);
612 Sema *sema = new Sema(PP, Clang->getASTContext(), consumer);
614 if (!autodetect) {
615 PP.AddPragmaHandler(new PragmaScopHandler(loc));
616 PP.AddPragmaHandler(new PragmaEndScopHandler(loc));
617 PP.AddPragmaHandler(new PragmaLiveOutHandler(*sema, live_out));
620 dim = isl_space_params_alloc(ctx, 0);
621 context = isl_set_universe(isl_space_copy(dim));
622 context_value = isl_set_universe(dim);
623 PP.AddPragmaHandler(new PragmaParameterHandler(*sema, context,
624 context_value));
625 PP.AddPragmaHandler(new PragmaValueBoundsHandler(ctx, *sema, value_bounds));
627 Diags.getClient()->BeginSourceFile(Clang->getLangOpts(), &PP);
628 ParseAST(*sema);
629 Diags.getClient()->EndSourceFile();
631 delete sema;
632 delete Clang;
633 llvm::llvm_shutdown();
635 if (consumer.scop) {
636 consumer.scop->context = isl_set_intersect(context,
637 consumer.scop->context);
638 consumer.scop->context_value = isl_set_intersect(context_value,
639 consumer.scop->context_value);
640 } else {
641 isl_set_free(context);
642 isl_set_free(context_value);
645 update_arrays(consumer.scop, value_bounds, live_out);
647 for (vb_it = value_bounds.begin(); vb_it != value_bounds.end(); vb_it++)
648 isl_set_free(vb_it->second);
650 return consumer.scop;