Polly 23.0.0git
extract_interface.cc
Go to the documentation of this file.
1/*
2 * Copyright 2011 Sven Verdoolaege. 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.
10 *
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.
15 *
16 * THIS SOFTWARE IS PROVIDED BY SVEN VERDOOLAEGE ''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 SVEN VERDOOLAEGE 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.
27 *
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 * Sven Verdoolaege.
32 */
33
34#include "isl_config.h"
35#undef PACKAGE
36
37#include <assert.h>
38#include <iostream>
39#include <stdlib.h>
40#include <type_traits>
41#ifdef HAVE_ADT_OWNINGPTR_H
42#include <llvm/ADT/OwningPtr.h>
43#else
44#include <memory>
45#endif
46#ifdef HAVE_LLVM_OPTION_ARG_H
47#include <llvm/Option/Arg.h>
48#endif
49#include <llvm/Support/raw_ostream.h>
50#include <llvm/Support/CommandLine.h>
51#ifdef HAVE_TARGETPARSER_HOST_H
52#include <llvm/TargetParser/Host.h>
53#else
54#include <llvm/Support/Host.h>
55#endif
56#include <llvm/Support/ManagedStatic.h>
57#include <clang/AST/ASTContext.h>
58#include <clang/AST/ASTConsumer.h>
59#include <clang/Basic/Builtins.h>
60#include <clang/Basic/FileSystemOptions.h>
61#include <clang/Basic/FileManager.h>
62#include <clang/Basic/TargetOptions.h>
63#include <clang/Basic/TargetInfo.h>
64#include <clang/Basic/Version.h>
65#include <clang/Driver/Compilation.h>
66#include <clang/Driver/Driver.h>
67#include <clang/Driver/Tool.h>
68#include <clang/Frontend/CompilerInstance.h>
69#include <clang/Frontend/CompilerInvocation.h>
70#ifdef HAVE_BASIC_DIAGNOSTICOPTIONS_H
71#include <clang/Basic/DiagnosticOptions.h>
72#else
73#include <clang/Frontend/DiagnosticOptions.h>
74#endif
75#include <clang/Frontend/TextDiagnosticPrinter.h>
76#include <clang/Frontend/Utils.h>
77#include <clang/Lex/HeaderSearch.h>
78#ifdef HAVE_LEX_PREPROCESSOROPTIONS_H
79#include <clang/Lex/PreprocessorOptions.h>
80#else
81#include <clang/Frontend/PreprocessorOptions.h>
82#endif
83#include <clang/Lex/Preprocessor.h>
84#include <clang/Parse/ParseAST.h>
85#include <clang/Sema/Sema.h>
86
87#include "extract_interface.h"
88#include "generator.h"
89#include "python.h"
90#include "plain_cpp.h"
91#include "cpp_conversion.h"
92#include "template_cpp.h"
93
94using namespace std;
95using namespace clang;
96using namespace clang::driver;
97#ifdef HAVE_LLVM_OPTION_ARG_H
98using namespace llvm::opt;
99#endif
100
101#ifdef HAVE_ADT_OWNINGPTR_H
102#define unique_ptr llvm::OwningPtr
103#endif
104
105static llvm::cl::opt<string> InputFilename(llvm::cl::Positional,
106 llvm::cl::Required, llvm::cl::desc("<input file>"));
107static llvm::cl::list<string> Includes("I",
108 llvm::cl::desc("Header search path"),
109 llvm::cl::value_desc("path"), llvm::cl::Prefix);
110
111static llvm::cl::opt<string> OutputLanguage(llvm::cl::Required,
112 llvm::cl::ValueRequired, "language",
113 llvm::cl::desc("Bindings to generate"),
114 llvm::cl::value_desc("name"));
115
116static const char *ResourceDir =
117 CLANG_PREFIX "/lib/clang/" CLANG_VERSION_STRING;
118
119/* Does decl have an attribute of the following form?
120 *
121 * __attribute__((annotate("name")))
122 */
123bool has_annotation(Decl *decl, const char *name)
124{
125 if (!decl->hasAttrs())
126 return false;
127
128 AttrVec attrs = decl->getAttrs();
129 for (AttrVec::const_iterator i = attrs.begin() ; i != attrs.end(); ++i) {
130 const AnnotateAttr *ann = dyn_cast<AnnotateAttr>(*i);
131 if (!ann)
132 continue;
133 if (ann->getAnnotation().str() == name)
134 return true;
135 }
136
137 return false;
138}
139
140/* Is decl marked as exported?
141 */
142static bool is_exported(Decl *decl)
143{
144 return has_annotation(decl, "isl_export");
145}
146
147/* Collect all types and functions that are annotated "isl_export"
148 * in "exported_types" and "exported_function". Collect all function
149 * declarations in "functions".
150 *
151 * We currently only consider single declarations.
152 */
153struct MyASTConsumer : public ASTConsumer {
157
158 virtual HandleTopLevelDeclReturn HandleTopLevelDecl(DeclGroupRef D) {
159 Decl *decl;
160
161 if (!D.isSingleDecl())
162 return HandleTopLevelDeclContinue;
163 decl = D.getSingleDecl();
164 if (isa<FunctionDecl>(decl))
165 functions.insert(cast<FunctionDecl>(decl));
166 if (!is_exported(decl))
167 return HandleTopLevelDeclContinue;
168 switch (decl->getKind()) {
169 case Decl::Record:
170 exported_types.insert(cast<RecordDecl>(decl));
171 break;
172 case Decl::Function:
173 exported_functions.insert(cast<FunctionDecl>(decl));
174 break;
175 default:
176 break;
177 }
178 return HandleTopLevelDeclContinue;
179 }
180};
181
182#ifdef USE_ARRAYREF
183
184#ifdef HAVE_CXXISPRODUCTION
185static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
186{
187 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
188 "", false, false, Diags);
189}
190#elif defined(HAVE_ISPRODUCTION)
191static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
192{
193 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
194 "", false, Diags);
195}
196#elif defined(DRIVER_CTOR_TAKES_DEFAULTIMAGENAME)
197static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
198{
199 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
200 "", Diags);
201}
202#else
203static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
204{
205 return new Driver(binary, llvm::sys::getDefaultTargetTriple(), Diags);
206}
207#endif
208
209namespace clang { namespace driver { class Job; } }
210
211/* Clang changed its API from 3.5 to 3.6 and once more in 3.7.
212 * We fix this with a simple overloaded function here.
213 */
214struct ClangAPI {
215 static Job *command(Job *J) { return J; }
216 static Job *command(Job &J) { return &J; }
217 static Command *command(Command &C) { return &C; }
218};
219
220#ifdef CREATE_FROM_ARGS_TAKES_ARRAYREF
221
222/* Call CompilerInvocation::CreateFromArgs with the right arguments.
223 * In this case, an ArrayRef<const char *>.
224 */
225static void create_from_args(CompilerInvocation &invocation,
226 const ArgStringList *args, DiagnosticsEngine &Diags)
227{
228 CompilerInvocation::CreateFromArgs(invocation, *args, Diags);
229}
230
231#else
232
233/* Call CompilerInvocation::CreateFromArgs with the right arguments.
234 * In this case, two "const char *" pointers.
235 */
236static void create_from_args(CompilerInvocation &invocation,
237 const ArgStringList *args, DiagnosticsEngine &Diags)
238{
239 CompilerInvocation::CreateFromArgs(invocation, args->data() + 1,
240 args->data() + args->size(),
241 Diags);
242}
243
244#endif
245
246#ifdef CLANG_SYSROOT
247/* Set sysroot if required.
248 *
249 * If CLANG_SYSROOT is defined, then set it to this value.
250 */
251static void set_sysroot(ArgStringList &args)
252{
253 args.push_back("-isysroot");
254 args.push_back(CLANG_SYSROOT);
255}
256#else
257/* Set sysroot if required.
258 *
259 * If CLANG_SYSROOT is not defined, then it does not need to be set.
260 */
261static void set_sysroot(ArgStringList &args)
262{
263}
264#endif
265
266/* Create a CompilerInvocation object that stores the command line
267 * arguments constructed by the driver.
268 * The arguments are mainly useful for setting up the system include
269 * paths on newer clangs and on some platforms.
270 */
271static CompilerInvocation *construct_invocation(const char *filename,
272 DiagnosticsEngine &Diags)
273{
274 const char *binary = CLANG_PREFIX"/bin/clang";
275 const unique_ptr<Driver> driver(construct_driver(binary, Diags));
276 std::vector<const char *> Argv;
277 Argv.push_back(binary);
278 Argv.push_back(filename);
279 const unique_ptr<Compilation> compilation(
280 driver->BuildCompilation(llvm::ArrayRef<const char *>(Argv)));
281 JobList &Jobs = compilation->getJobs();
282
283 Command *cmd = cast<Command>(ClangAPI::command(*Jobs.begin()));
284 if (strcmp(cmd->getCreator().getName(), "clang"))
285 return NULL;
286
287 ArgStringList args = cmd->getArguments();
288 set_sysroot(args);
289
290 CompilerInvocation *invocation = new CompilerInvocation;
291 create_from_args(*invocation, &args, Diags);
292 return invocation;
293}
294
295#else
296
297static CompilerInvocation *construct_invocation(const char *filename,
298 DiagnosticsEngine &Diags)
299{
300 return NULL;
301}
302
303#endif
304
305#ifdef HAVE_BASIC_DIAGNOSTICOPTIONS_H
306
307static TextDiagnosticPrinter *construct_printer(void)
308{
309 return new TextDiagnosticPrinter(llvm::errs(), new DiagnosticOptions());
310}
311
312#else
313
314static TextDiagnosticPrinter *construct_printer(void)
315{
316 DiagnosticOptions DO;
317 return new TextDiagnosticPrinter(llvm::errs(), DO);
318}
319
320#endif
321
322#ifdef CREATETARGETINFO_TAKES_SHARED_PTR
323
324static TargetInfo *create_target_info(CompilerInstance *Clang,
325 DiagnosticsEngine &Diags)
326{
327 shared_ptr<TargetOptions> TO = Clang->getInvocation().TargetOpts;
328 TO->Triple = llvm::sys::getDefaultTargetTriple();
329 return TargetInfo::CreateTargetInfo(Diags, TO);
330}
331
332#elif defined(CREATETARGETINFO_TAKES_POINTER)
333
334static TargetInfo *create_target_info(CompilerInstance *Clang,
335 DiagnosticsEngine &Diags)
336{
337 TargetOptions &TO = Clang->getTargetOpts();
338 TO.Triple = llvm::sys::getDefaultTargetTriple();
339 return TargetInfo::CreateTargetInfo(Diags, &TO);
340}
341
342#else
343
344static TargetInfo *create_target_info(CompilerInstance *Clang,
345 DiagnosticsEngine &Diags)
346{
347 TargetOptions &TO = Clang->getTargetOpts();
348 TO.Triple = llvm::sys::getDefaultTargetTriple();
349 return TargetInfo::CreateTargetInfo(Diags, TO);
350}
351
352#endif
353
354#ifdef CREATEDIAGNOSTICS_TAKES_ARG
355
356static void create_diagnostics(CompilerInstance *Clang)
357{
358 Clang->createDiagnostics(0, NULL, construct_printer());
359}
360
361#else
362
363static void create_diagnostics(CompilerInstance *Clang)
364{
365 Clang->createDiagnostics(construct_printer());
366}
367
368#endif
369
370#ifdef CREATEPREPROCESSOR_TAKES_TUKIND
371
372static void create_preprocessor(CompilerInstance *Clang)
373{
374 Clang->createPreprocessor(TU_Complete);
375}
376
377#else
378
379static void create_preprocessor(CompilerInstance *Clang)
380{
381 Clang->createPreprocessor();
382}
383
384#endif
385
386#ifdef ADDPATH_TAKES_4_ARGUMENTS
387
388/* Add "Path" to the header search options.
389 *
390 * Do not take into account sysroot, i.e., set ignoreSysRoot to true.
391 */
392void add_path(HeaderSearchOptions &HSO, string Path)
393{
394 HSO.AddPath(Path, frontend::Angled, false, true);
395}
396
397#else
398
399/* Add "Path" to the header search options.
400 *
401 * Do not take into account sysroot, i.e., set IsSysRootRelative to false.
402 */
403void add_path(HeaderSearchOptions &HSO, string Path)
404{
405 HSO.AddPath(Path, frontend::Angled, true, false, false);
406}
407
408#endif
409
410#ifdef HAVE_SETMAINFILEID
411
412template <typename T>
413static void create_main_file_id(SourceManager &SM, const T &file)
414{
415 SM.setMainFileID(SM.createFileID(file, SourceLocation(),
416 SrcMgr::C_User));
417}
418
419#else
420
421static void create_main_file_id(SourceManager &SM, const FileEntry *file)
422{
423 SM.createMainFileID(file);
424}
425
426#endif
427
428#ifdef SETLANGDEFAULTS_TAKES_5_ARGUMENTS
429
431
432static void set_lang_defaults(CompilerInstance *Clang)
433{
434 PreprocessorOptions &PO = Clang->getPreprocessorOpts();
435 TargetOptions &TO = Clang->getTargetOpts();
436 llvm::Triple T(TO.Triple);
437 SETLANGDEFAULTS::setLangDefaults(Clang->getLangOpts(), IK_C, T,
439 LangStandard::lang_unspecified);
440}
441
442#else
443
444static void set_lang_defaults(CompilerInstance *Clang)
445{
446 CompilerInvocation::setLangDefaults(Clang->getLangOpts(), IK_C,
447 LangStandard::lang_unspecified);
448}
449
450#endif
451
452#ifdef SETINVOCATION_TAKES_SHARED_PTR
453
454static void set_invocation(CompilerInstance *Clang,
455 CompilerInvocation *invocation)
456{
457 Clang->setInvocation(std::make_shared<CompilerInvocation>(*invocation));
458}
459
460#else
461
462static void set_invocation(CompilerInstance *Clang,
463 CompilerInvocation *invocation)
464{
465 Clang->setInvocation(invocation);
466}
467
468#endif
469
470/* Helper function for ignore_error that only gets enabled if T
471 * (which is either const FileEntry * or llvm::ErrorOr<const FileEntry *>)
472 * has getError method, i.e., if it is llvm::ErrorOr<const FileEntry *>.
473 */
474template <class T>
475static const FileEntry *ignore_error_helper(const T obj, int,
476 int[1][sizeof(obj.getError())])
477{
478 return *obj;
479}
480
481/* Helper function for ignore_error that is always enabled,
482 * but that only gets selected if the variant above is not enabled,
483 * i.e., if T is const FileEntry *.
484 */
485template <class T>
486static const FileEntry *ignore_error_helper(const T obj, long, void *)
487{
488 return obj;
489}
490
491/* Given either a const FileEntry * or a llvm::ErrorOr<const FileEntry *>,
492 * extract out the const FileEntry *.
493 */
494template <class T>
495static const FileEntry *ignore_error(const T obj)
496{
497 return ignore_error_helper(obj, 0, NULL);
498}
499
500/* This is identical to std::void_t in C++17.
501 */
502template< class... >
503using void_t = void;
504
505/* A template class with value true if "T" has a getFileRef method.
506 */
507template <class T, class = void>
508struct HasGetFileRef : public std::false_type {};
509template <class T>
510struct HasGetFileRef<T, ::void_t<decltype(&T::getFileRef)>> :
511 public std::true_type {};
512
513/* Return the FileEntryRef/FileEntry corresponding to the given file name
514 * in the given compiler instances, ignoring any error.
515 *
516 * If T (= FileManager) has a getFileRef method, then call that and
517 * return a FileEntryRef.
518 * Otherwise, call getFile and return a FileEntry (pointer).
519 */
520template <typename T,
521 typename std::enable_if<HasGetFileRef<T>::value, bool>::type = true>
522static auto getFile(T& obj, const std::string &filename)
523 -> decltype(*obj.getFileRef(filename))
524{
525 auto file = obj.getFileRef(filename);
526 assert(file);
527 return *file;
528}
529template <typename T,
530 typename std::enable_if<!HasGetFileRef<T>::value, bool>::type = true>
531static const FileEntry *getFile(T& obj, const std::string &filename)
532{
533 const FileEntry *file = ignore_error(obj.getFile(filename));
534 assert(file);
535 return file;
536}
537
538/* Create an interface generator for the selected language and
539 * then use it to generate the interface.
540 */
541static void generate(MyASTConsumer &consumer, SourceManager &SM)
542{
543 generator *gen;
544
545 if (OutputLanguage.compare("python") == 0) {
546 gen = new python_generator(SM, consumer.exported_types,
547 consumer.exported_functions, consumer.functions);
548 } else if (OutputLanguage.compare("cpp") == 0) {
549 gen = new plain_cpp_generator(SM, consumer.exported_types,
550 consumer.exported_functions, consumer.functions);
551 } else if (OutputLanguage.compare("cpp-checked") == 0) {
552 gen = new plain_cpp_generator(SM, consumer.exported_types,
553 consumer.exported_functions, consumer.functions, true);
554 } else if (OutputLanguage.compare("cpp-checked-conversion") == 0) {
555 gen = new cpp_conversion_generator(SM, consumer.exported_types,
556 consumer.exported_functions, consumer.functions);
557 } else if (OutputLanguage.compare("template-cpp") == 0) {
558 gen = new template_cpp_generator(SM, consumer.exported_types,
559 consumer.exported_functions, consumer.functions);
560 } else {
561 cerr << "Language '" << OutputLanguage
562 << "' not recognized." << endl
563 << "Not generating bindings." << endl;
564 exit(EXIT_FAILURE);
565 }
566
567 gen->generate();
568}
569
570int main(int argc, char *argv[])
571{
572 llvm::cl::ParseCommandLineOptions(argc, argv);
573
574 CompilerInstance *Clang = new CompilerInstance();
575 create_diagnostics(Clang);
576 DiagnosticsEngine &Diags = Clang->getDiagnostics();
577 Diags.setSuppressSystemWarnings(true);
578 TargetInfo *target = create_target_info(Clang, Diags);
579 Clang->setTarget(target);
580 set_lang_defaults(Clang);
581 CompilerInvocation *invocation =
582 construct_invocation(InputFilename.c_str(), Diags);
583 if (invocation)
584 set_invocation(Clang, invocation);
585 Clang->createFileManager();
586 Clang->createSourceManager(Clang->getFileManager());
587 HeaderSearchOptions &HSO = Clang->getHeaderSearchOpts();
588 LangOptions &LO = Clang->getLangOpts();
589 PreprocessorOptions &PO = Clang->getPreprocessorOpts();
590 HSO.ResourceDir = ResourceDir;
591
592 for (llvm::cl::list<string>::size_type i = 0; i < Includes.size(); ++i)
593 add_path(HSO, Includes[i]);
594
595 PO.addMacroDef("__isl_give=__attribute__((annotate(\"isl_give\")))");
596 PO.addMacroDef("__isl_keep=__attribute__((annotate(\"isl_keep\")))");
597 PO.addMacroDef("__isl_take=__attribute__((annotate(\"isl_take\")))");
598 PO.addMacroDef("__isl_export=__attribute__((annotate(\"isl_export\")))");
599 PO.addMacroDef("__isl_overload="
600 "__attribute__((annotate(\"isl_overload\"))) "
601 "__attribute__((annotate(\"isl_export\")))");
602 PO.addMacroDef("__isl_constructor=__attribute__((annotate(\"isl_constructor\"))) __attribute__((annotate(\"isl_export\")))");
603 PO.addMacroDef("__isl_subclass(super)=__attribute__((annotate(\"isl_subclass(\" #super \")\"))) __attribute__((annotate(\"isl_export\")))");
604
605 create_preprocessor(Clang);
606 Preprocessor &PP = Clang->getPreprocessor();
607
608 PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(), LO);
609
610 auto file = getFile(Clang->getFileManager(), InputFilename);
611 create_main_file_id(Clang->getSourceManager(), file);
612
613 Clang->createASTContext();
614 MyASTConsumer consumer;
615 Sema *sema = new Sema(PP, Clang->getASTContext(), consumer);
616
617 Diags.getClient()->BeginSourceFile(LO, &PP);
618 ParseAST(*sema);
619 Diags.getClient()->EndSourceFile();
620
621 generate(consumer, Clang->getSourceManager());
622
623 delete sema;
624 delete Clang;
625 llvm::llvm_shutdown();
626
627 if (Diags.hasErrorOccurred())
628 return EXIT_FAILURE;
629 return EXIT_SUCCESS;
630}
virtual void generate()=0
bool has_annotation(Decl *decl, const char *name)
static auto getFile(T &obj, const std::string &filename) -> decltype(*obj.getFileRef(filename))
static void create_preprocessor(CompilerInstance *Clang)
static const FileEntry * ignore_error_helper(const T obj, int, int[1][sizeof(obj.getError())])
void add_path(HeaderSearchOptions &HSO, string Path)
static bool is_exported(Decl *decl)
static void set_lang_defaults(CompilerInstance *Clang)
static void generate(MyASTConsumer &consumer, SourceManager &SM)
static TextDiagnosticPrinter * construct_printer(void)
static llvm::cl::list< string > Includes("I", llvm::cl::desc("Header search path"), llvm::cl::value_desc("path"), llvm::cl::Prefix)
static llvm::cl::opt< string > InputFilename(llvm::cl::Positional, llvm::cl::Required, llvm::cl::desc("<input file>"))
static void create_diagnostics(CompilerInstance *Clang)
static llvm::cl::opt< string > OutputLanguage(llvm::cl::Required, llvm::cl::ValueRequired, "language", llvm::cl::desc("Bindings to generate"), llvm::cl::value_desc("name"))
static const char * ResourceDir
static TargetInfo * create_target_info(CompilerInstance *Clang, DiagnosticsEngine &Diags)
void void_t
static const FileEntry * ignore_error(const T obj)
static void set_invocation(CompilerInstance *Clang, CompilerInvocation *invocation)
static void create_main_file_id(SourceManager &SM, const FileEntry *file)
static CompilerInvocation * construct_invocation(const char *filename, DiagnosticsEngine &Diags)
#define C(FN,...)
Definition isl_test2.cc:259
enum isl_fold type
Definition isl_test.c:3867
const char * set
Definition isl_test.c:1364
const char * name
Definition isl_test.c:10749
const char * obj
Definition isl_test.c:3166
#define assert(exp)
set< FunctionDecl * > functions
set< RecordDecl * > exported_types
set< FunctionDecl * > exported_functions
virtual HandleTopLevelDeclReturn HandleTopLevelDecl(DeclGroupRef D)