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