1 /************************************************************************
2  ************************************************************************
3     FAUST compiler
4     Copyright (C) 2003-2018 GRAME, Centre National de Creation Musicale
5     ---------------------------------------------------------------------
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  ************************************************************************
20  ************************************************************************/
21 
22 #include "compatibility.hh"
23 
24 #if CLANG_BUILD
25 
26 #include "clang_code_container.hh"
27 
28 #include <clang/CodeGen/CodeGenAction.h>
29 #include <clang/Driver/Compilation.h>
30 #include <clang/Driver/Driver.h>
31 #include <clang/Driver/Tool.h>
32 #include <clang/Frontend/CompilerInstance.h>
33 #include <clang/Frontend/CompilerInvocation.h>
34 #include <clang/Frontend/FrontendDiagnostic.h>
35 #include <clang/Frontend/TextDiagnosticPrinter.h>
36 
37 #include <llvm/Support/Host.h>
38 #include <llvm/Support/Path.h>
39 #include <llvm/Support/TargetSelect.h>
40 #include <llvm/Support/raw_ostream.h>
41 
42 #include <llvm/Bitcode/ReaderWriter.h>
43 #if defined(LLVM_34) || defined(LLVM_35) || defined(LLVM_36) || defined(LLVM_37) || defined(LLVM_38) || \
44     defined(LLVM_39) || defined(LLVM_40)
45 #include <llvm/IR/Module.h>
46 #else
47 #include <llvm/Module.h>
48 #endif
49 
50 #if defined(LLVM_35) || defined(LLVM_36) || defined(LLVM_37) || defined(LLVM_38) || defined(LLVM_39) || defined(LLVM_40)
51 #include <llvm/Support/FileSystem.h>
52 #define sysfs_binary_flag sys::fs::F_None
53 #elif defined(LLVM_34)
54 #define sysfs_binary_flag sys::fs::F_Binary
55 #else
56 #define sysfs_binary_flag raw_fd_ostream::F_Binary
57 #endif
58 
59 using namespace std;
60 using namespace llvm;
61 using namespace clang;
62 using namespace clang::driver;
63 
64 #include "CInterface_exp.h"
65 //#include "scheduler_exp.h"
66 
67 // Helper functions
68 bool    linkModules(Module* dst, Module* src, char* error_msg);
69 Module* loadModule(const string& module_name, llvm::LLVMContext* context);
70 Module* linkAllModules(llvm::LLVMContext* context, Module* dst, char* error);
71 
72 #define FAUST_FILENAME "/var/tmp/FaustLLVM.c"
73 
ClangCodeContainer(const string & name,int numInputs,int numOutputs)74 ClangCodeContainer::ClangCodeContainer(const string& name, int numInputs, int numOutputs)
75 {
76     fOut = new ofstream(getTempName());
77 
78     if (gGlobal->gFloatSize == 2) {
79         *fOut << "#define FAUSTFLOAT double"
80               << "\n\n";
81     }
82 
83     *fOut << ___architecture_faust_gui_CUI_h;
84     // fOut << ___architecture_scheduler_cpp;
85     *fOut << endl;
86     *fOut << "#define max(a,b) ((a < b) ? b : a)" << endl;
87     *fOut << "#define min(a,b) ((a < b) ? a : b)"
88           << "\n\n";
89     printHeader(*fOut);
90 
91     fContainer = CCodeContainer::createContainer(name, numInputs, numOutputs, fOut);
92 
93     if (gGlobal->gVectorSwitch) {
94         fCompiler = new DAGInstructionsCompiler(fContainer);
95     } else {
96         fCompiler = new InstructionsCompiler(fContainer);
97     }
98 
99     if (gGlobal->gPrintXMLSwitch) fCompiler->setDescription(new Description());
100     if (gGlobal->gPrintDocSwitch) fCompiler->setDescription(new Description());
101 }
102 
~ClangCodeContainer()103 ClangCodeContainer::~ClangCodeContainer()
104 {
105     delete fOut;
106 }
107 
produceModule(Tree signals,const string & filename)108 LLVMResult* ClangCodeContainer::produceModule(Tree signals, const string& filename)
109 {
110     // Compile DSP and generate C code
111     fCompiler->compileMultiSignal(signals);
112     fContainer->produceClass();
113 
114 #if defined(LLVM_34) || defined(LLVM_35) || defined(LLVM_36) || defined(LLVM_37) || defined(LLVM_38) || \
115     defined(LLVM_39) || defined(LLVM_40)
116     // Compile it with 'clang'
117     IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts   = new DiagnosticOptions();
118     TextDiagnosticPrinter*                DiagClient = new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
119 
120     IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
121     DiagnosticsEngine                 Diags(DiagID, &*DiagOpts, DiagClient);
122     Driver                            TheDriver("", llvm::sys::getProcessTriple(), "a.out", Diags);
123     TheDriver.setTitle("clang interpreter");
124 
125     int         argc = 2;
126     const char* argv[argc + 1];
127     argv[0]    = "clang";
128     argv[1]    = getTempName();
129     argv[argc] = 0;  // NULL terminated argv
130     SmallVector<const char*, 16> Args(argv, argv + argc);
131     Args.push_back("-fsyntax-only");
132     // Args.push_back("-O3");
133     Args.push_back("-ffast-math");
134 
135     for (const auto& it : gGlobal->gImportDirList) {
136         string path = "-I" + it;
137         Args.push_back(strdup(path.c_str()));
138     }
139 
140     OwningPtr<Compilation> C(TheDriver.BuildCompilation(Args));
141     if (!C) {
142         return nullptr;
143     }
144 
145     const driver::JobList& Jobs = C->getJobs();
146     if (Jobs.size() != 1 || !isa<driver::Command>(*Jobs.begin())) {
147         SmallString<256>          Msg;
148         llvm::raw_svector_ostream OS(Msg);
149         Jobs.Print(OS, "; ", true);
150         Diags.Report(diag::err_fe_expected_compiler_job) << OS.str();
151         return nullptr;
152     }
153 
154     const driver::Command* Cmd = cast<driver::Command>(*Jobs.begin());
155     if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") {
156         Diags.Report(diag::err_fe_expected_clang_command);
157         return nullptr;
158     }
159 
160     // Initialize a compiler invocation object from the clang (-cc1) arguments.
161     const driver::ArgStringList&  CCArgs = Cmd->getArguments();
162     OwningPtr<CompilerInvocation> CI(new CompilerInvocation);
163     CompilerInvocation::CreateFromArgs(*CI, const_cast<const char**>(CCArgs.data()),
164                                        const_cast<const char**>(CCArgs.data()) + CCArgs.size(), Diags);
165 
166     // Create a compiler instance to handle the actual work.
167     CompilerInstance Clang;
168     Clang.setInvocation(CI.take());
169 
170     // Create the compilers actual diagnostics engine.
171     Clang.createDiagnostics();
172     if (!Clang.hasDiagnostics()) {
173         return nullptr;
174     }
175 
176     CompilerInvocation::setLangDefaults(Clang.getLangOpts(), IK_CXX, LangStandard::lang_unspecified);
177 
178     // Create and execute the frontend to generate an LLVM bitcode module.
179     OwningPtr<CodeGenAction> Act(new EmitLLVMOnlyAction());
180     if (!Clang.ExecuteAction(*Act)) {
181         return nullptr;
182     }
183 
184     // Get the compiled LLVM module
185     if (llvm::Module* Module = Act->takeModule()) {
186         LLVMResult* result = static_cast<LLVMResult*>(calloc(1, sizeof(LLVMResult)));
187         result->fModule    = Module;
188         result->fContext   = Act->takeLLVMContext();
189 
190         // Link LLVM modules defined in 'ffunction'
191         set<string>           S;
192         set<string>::iterator f;
193         char                  error_msg[512];
194 
195         collectLibrary(S);
196         if (S.size() > 0) {
197             for (f = S.begin(); f != S.end(); f++) {
198                 string module_name = unquote(*f);
199                 if (endWith(module_name, ".bc") || endWith(module_name, ".ll")) {
200                     Module* module = loadModule(module_name, result->fContext);
201                     if (module) {
202                         bool res = linkModules(result->fModule, module, error_msg);
203                         if (!res) printf("Link LLVM modules %s\n", error_msg);
204                     }
205                 }
206             }
207         }
208 
209         // Possibly link with additional LLVM modules
210         char error[256];
211         if (!linkAllModules(result->fContext, result->fModule, error)) {
212             stringstream llvm_error;
213             llvm_error << "ERROR : " << error << endl;
214             throw faustexception(llvm_error.str());
215         }
216 
217         // Keep source files pathnames
218         result->fPathnameList = gGlobal->gReader.listSrcFiles();
219 
220         // Possibly output file
221         if (filename != "") {
222             string         err;
223             raw_fd_ostream out(filename.c_str(), err, sysfs_binary_flag);
224             WriteBitcodeToFile(result->fModule, out);
225         }
226 
227         return result;
228     } else {
229         return nullptr;
230     }
231 #else
232     return nullptr;
233 #endif
234 }
235 
createContainer(const string & name,int numInputs,int numOutputs)236 CodeContainer* ClangCodeContainer::createContainer(const string& name, int numInputs, int numOutputs)
237 {
238     return new ClangCodeContainer(name, numInputs, numOutputs);
239 }
240 
241 #endif  // CLANG_BUILD
242