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 #ifdef WIN32
23 #pragma warning(disable : 4146 4244 4275)
24 #endif
25 
26 #include "llvm_code_container.hh"
27 #include "compatibility.hh"
28 #include "exception.hh"
29 #include "fir_to_fir.hh"
30 #include "global.hh"
31 #include "llvm_dynamic_dsp_aux.hh"
32 #include "llvm_instructions.hh"
33 
34 /*
35  LLVM module description:
36 
37 - 'clone' method is implemented in the 'llvm_dsp' wrapping code
38 
39  TODO: in -mem mode, classInit and classDestroy will have to be called once at factory init and destroy time
40 */
41 
42 // Helper functions
43 bool      linkModules(Module* dst, ModulePTR src, string& error);
44 ModulePTR loadModule(const string& module_name, LLVMContext* context);
45 Module*   linkAllModules(LLVMContext* context, Module* dst, string& error);
46 
createScalarContainer(const string & name,int sub_container_type)47 CodeContainer* LLVMCodeContainer::createScalarContainer(const string& name, int sub_container_type)
48 {
49     return new LLVMScalarCodeContainer(name, 0, 1, fModule, fContext, sub_container_type);
50 }
51 
LLVMCodeContainer(const string & name,int numInputs,int numOutputs)52 LLVMCodeContainer::LLVMCodeContainer(const string& name, int numInputs, int numOutputs)
53 {
54     LLVMContext* context = new LLVMContext();
55     stringstream compile_options;
56     gGlobal->printCompilationOptions(compile_options);
57     Module* module = new Module(compile_options.str() + ", v" + string(FAUSTVERSION), *context);
58 
59     init(name, numInputs, numOutputs, module, context);
60 }
61 
LLVMCodeContainer(const string & name,int numInputs,int numOutputs,Module * module,LLVMContext * context)62 LLVMCodeContainer::LLVMCodeContainer(const string& name, int numInputs, int numOutputs, Module* module,
63                                      LLVMContext* context)
64 {
65     init(name, numInputs, numOutputs, module, context);
66  }
67 
init(const string & name,int numInputs,int numOutputs,Module * module,LLVMContext * context)68 void LLVMCodeContainer::init(const string& name, int numInputs, int numOutputs, Module* module,
69                              LLVMContext* context)
70 {
71     initialize(numInputs, numOutputs);
72 
73     fKlassName = name;
74     fModule    = module;
75     fContext   = context;
76     fBuilder   = new IRBuilder<>(*fContext);
77 
78     // Set "-fast-math"
79     FastMathFlags FMF;
80 #if defined(LLVM_80) || defined(LLVM_90) || defined(LLVM_100) || defined(LLVM_110) || defined(LLVM_120) || \
81     defined(LLVM_130) || defined(LLVM_140)
82     FMF.setFast();  // has replaced the following function
83 #else
84     FMF.setUnsafeAlgebra();
85 #endif
86     fBuilder->setFastMathFlags(FMF);
87     fModule->setTargetTriple(sys::getDefaultTargetTriple());
88 }
89 
~LLVMCodeContainer()90 LLVMCodeContainer::~LLVMCodeContainer()
91 {
92     delete fBuilder;
93 }
94 
createContainer(const string & name,int numInputs,int numOutputs)95 CodeContainer* LLVMCodeContainer::createContainer(const string& name, int numInputs, int numOutputs)
96 {
97     gGlobal->gDSPStruct = true;
98     CodeContainer* container;
99 
100     if (gGlobal->gFloatSize == 3) {
101         throw faustexception("ERROR : quad format not supported for LLVM\n");
102     }
103     if (gGlobal->gOpenCLSwitch) {
104         throw faustexception("ERROR : OpenCL not supported for LLVM\n");
105     }
106     if (gGlobal->gCUDASwitch) {
107         throw faustexception("ERROR : CUDA not supported for LLVM\n");
108     }
109 
110     if (gGlobal->gOpenMPSwitch) {
111         throw faustexception("ERROR : OpenMP not supported for LLVM\n");
112     } else if (gGlobal->gSchedulerSwitch) {
113         container = new LLVMWorkStealingCodeContainer(name, numInputs, numOutputs);
114     } else if (gGlobal->gVectorSwitch) {
115         container = new LLVMVectorCodeContainer(name, numInputs, numOutputs);
116     } else {
117         container = new LLVMScalarCodeContainer(name, numInputs, numOutputs);
118     }
119 
120     return container;
121 }
122 
generateDspStruct()123 PointerType* LLVMCodeContainer::generateDspStruct()
124 {
125     // Generate DSP structure
126     LLVMTypeHelper type_helper(fModule);
127     generateDeclarations(&fStructVisitor);
128 
129     DeclareStructTypeInst* dec_type = fStructVisitor.getStructType(fKlassName);
130 
131     LLVMType dsp_type = type_helper.convertFIRType(dec_type->fType);
132     return PointerType::get(dsp_type, 0);
133 }
134 
generateFunMaps()135 void LLVMCodeContainer::generateFunMaps()
136 {
137     if (gGlobal->gFastMath) {
138         generateFunMap("fabs", "fast_fabs", 1);
139         generateFunMap("acos", "fast_acos", 1);
140         generateFunMap("asin", "fast_asin", 1);
141         generateFunMap("atan", "fast_atan", 1);
142         generateFunMap("atan2", "fast_atan2", 2);
143         generateFunMap("ceil", "fast_ceil", 1);
144         generateFunMap("cos", "fast_cos", 1);
145         generateFunMap("exp", "fast_exp", 1);
146         generateFunMap("exp2", "fast_exp2", 1);
147         generateFunMap("exp10", "fast_exp10", 1);
148         generateFunMap("floor", "fast_floor", 1);
149         generateFunMap("fmod", "fast_fmod", 2);
150         generateFunMap("log", "fast_log", 1);
151         generateFunMap("log2", "fast_log2", 1);
152         generateFunMap("log10", "fast_log10", 1);
153         generateFunMap("pow", "fast_pow", 2);
154         generateFunMap("remainder", "fast_remainder", 2);
155         generateFunMap("rint", "fast_rint", 1);
156         generateFunMap("round", "fast_round", 1);
157         generateFunMap("sin", "fast_sin", 1);
158         generateFunMap("sqrt", "fast_sqrt", 1);
159         generateFunMap("tan", "fast_tan", 1);
160     } else {
161 #ifdef __APPLE__
162         generateFunMap("exp10", "__exp10", 1, true);
163 #endif
164     }
165 }
166 
generateFunMap(const string & fun1_aux,const string & fun2_aux,int num_args,bool body)167 void LLVMCodeContainer::generateFunMap(const string& fun1_aux, const string& fun2_aux, int num_args, bool body)
168 {
169     Typed::VarType type = itfloat();
170     string fun1 = fun1_aux + isuffix();
171     string fun2 = fun2_aux + isuffix();
172 
173     list<NamedTyped*> args1;
174     list<ValueInst*>  args2;
175     for (int i = 0; i < num_args; i++) {
176         string var = gGlobal->getFreshID("val");
177         args1.push_back(InstBuilder::genNamedTyped(var, type));
178         args2.push_back(InstBuilder::genLoadFunArgsVar(var));
179     }
180 
181     // Creates function
182     FunTyped* fun_type1 = InstBuilder::genFunTyped(args1, InstBuilder::genBasicTyped(type), FunTyped::kLocal);
183     FunTyped* fun_type2 = InstBuilder::genFunTyped(args1, InstBuilder::genBasicTyped(type), FunTyped::kDefault);
184 
185     InstBuilder::genDeclareFunInst(fun2, fun_type2)->accept(fCodeProducer);
186     if (body) {
187         BlockInst* block = InstBuilder::genBlockInst();
188         block->pushBackInst(InstBuilder::genRetInst(InstBuilder::genFunCallInst(fun2, args2)));
189         InstBuilder::genDeclareFunInst(fun1, fun_type1, block)->accept(fCodeProducer);
190     }
191 }
192 
produceInternal()193 void LLVMCodeContainer::produceInternal()
194 {
195     // Generate DSP structure
196     fCodeProducer = new LLVMInstVisitor(fModule, fBuilder, &fStructVisitor, generateDspStruct());
197 
198     /// Memory methods
199     generateCalloc()->accept(fCodeProducer);
200     generateFree()->accept(fCodeProducer);
201 
202     generateNewDsp("new" + fKlassName, fStructVisitor.getStructSize())->accept(fCodeProducer);
203     generateDeleteDsp("delete" + fKlassName, "dsp")->accept(fCodeProducer);
204 
205     generateFunMaps();
206 
207     // Global declarations
208     generateExtGlobalDeclarations(fCodeProducer);
209     generateGlobalDeclarations(fCodeProducer);
210 
211     generateInstanceInitFun("instanceInit" + fKlassName, "dsp", false, false)->accept(fCodeProducer);
212     generateFillFun("fill" + fKlassName, "dsp", false, false)->accept(fCodeProducer);
213 }
214 
produceFactory()215 dsp_factory_base* LLVMCodeContainer::produceFactory()
216 {
217     // Generate gub containers
218     generateSubContainers();
219 
220     // Generate DSP structure
221     fCodeProducer = new LLVMInstVisitor(fModule, fBuilder, &fStructVisitor, generateDspStruct());
222 
223     generateFunMaps();
224 
225     // Global declarations
226     generateExtGlobalDeclarations(fCodeProducer);
227     generateGlobalDeclarations(fCodeProducer);
228 
229     generateStaticInitFun("classInit" + fKlassName, false)->accept(fCodeProducer);
230     generateInstanceClear("instanceClear" + fKlassName, "dsp", false, false)->accept(fCodeProducer);
231     generateInstanceConstants("instanceConstants" + fKlassName, "dsp", false, false)->accept(fCodeProducer);
232     generateAllocate("allocate" + fKlassName, "dsp", false, false)->accept(fCodeProducer);
233     generateDestroy("destroy" + fKlassName, "dsp", false, false)->accept(fCodeProducer);
234 
235     // generateGetJSON generation
236     if (gGlobal->gFloatSize == 1) {
237         generateGetJSON<float>();
238     } else {
239         generateGetJSON<double>();
240     }
241 
242     // Compute
243     generateCompute();
244 
245     // Link LLVM modules defined in 'ffunction'
246     set<string> S;
247     collectLibrary(S);
248     string error;
249 
250     if (S.size() > 0) {
251         for (const auto& f : S) {
252             string module_name = unquote(f);
253             if (endWith(module_name, ".bc") || endWith(module_name, ".ll")) {
254                 ModulePTR module = loadModule(module_name, fContext);
255                 if (module) {
256                     bool res = linkModules(fModule, MovePTR(module), error);
257                     if (!res) cerr << "WARNING : " << error << endl;
258                 }
259             }
260         }
261     }
262 
263     // Possibly link with additional LLVM modules
264     if (!linkAllModules(fContext, fModule, error)) {
265         throw faustexception("ERROR : " + error);
266     }
267 
268     return new llvm_dynamic_dsp_factory_aux("", fModule, fContext, "", -1);
269 }
270 
271 // Scalar
LLVMScalarCodeContainer(const string & name,int numInputs,int numOutputs)272 LLVMScalarCodeContainer::LLVMScalarCodeContainer(const string& name, int numInputs, int numOutputs)
273     : LLVMCodeContainer(name, numInputs, numOutputs)
274 {
275 }
276 
LLVMScalarCodeContainer(const string & name,int numInputs,int numOutputs,Module * module,LLVMContext * context,int sub_container_type)277 LLVMScalarCodeContainer::LLVMScalarCodeContainer(const string& name, int numInputs, int numOutputs, Module* module,
278                                                  LLVMContext* context, int sub_container_type)
279     : LLVMCodeContainer(name, numInputs, numOutputs, module, context)
280 {
281     fSubContainerType = sub_container_type;
282 }
283 
~LLVMScalarCodeContainer()284 LLVMScalarCodeContainer::~LLVMScalarCodeContainer()
285 {
286 }
287 
generateCompute()288 void LLVMScalarCodeContainer::generateCompute()
289 {
290     generateComputeFun("compute" + fKlassName, "dsp", false, false)->accept(fCodeProducer);
291 }
292 
generateComputeAux()293 BlockInst* LLVMScalarCodeContainer::generateComputeAux()
294 {
295     BlockInst* block = InstBuilder::genBlockInst();
296     // Control
297     block->pushBackInst(fComputeBlockInstructions);
298     // Generates the DSP loop
299     block->pushBackInst(fCurLoop->generateScalarLoop(fFullCount));
300     // Generates post DSP loop code
301     block->pushBackInst(fPostComputeBlockInstructions);
302     return block;
303 }
304 
305 // Vector
LLVMVectorCodeContainer(const string & name,int numInputs,int numOutputs)306 LLVMVectorCodeContainer::LLVMVectorCodeContainer(const string& name, int numInputs, int numOutputs)
307     : VectorCodeContainer(numInputs, numOutputs), LLVMCodeContainer(name, numInputs, numOutputs)
308 {
309 }
310 
~LLVMVectorCodeContainer()311 LLVMVectorCodeContainer::~LLVMVectorCodeContainer()
312 {
313 }
314 
generateCompute()315 void LLVMVectorCodeContainer::generateCompute()
316 {
317     // Possibly generate separated functions
318     generateComputeFunctions(fCodeProducer);
319 
320     generateComputeFun("compute" + fKlassName, "dsp", false, false)->accept(fCodeProducer);
321 }
322 
generateComputeAux()323 BlockInst* LLVMVectorCodeContainer::generateComputeAux()
324 {
325     BlockInst* block = InstBuilder::genBlockInst();
326     // Control
327     block->pushBackInst(fComputeBlockInstructions);
328     // Generates the DSP loop
329     block->pushBackInst(fDAGBlock);
330     return block;
331 }
332 
333 // OpenMP
LLVMOpenMPCodeContainer(const string & name,int numInputs,int numOutputs)334 LLVMOpenMPCodeContainer::LLVMOpenMPCodeContainer(const string& name, int numInputs, int numOutputs)
335     : OpenMPCodeContainer(numInputs, numOutputs), LLVMCodeContainer(name, numInputs, numOutputs)
336 {
337 }
338 
~LLVMOpenMPCodeContainer()339 LLVMOpenMPCodeContainer::~LLVMOpenMPCodeContainer()
340 {
341 }
342 
generateCompute()343 void LLVMOpenMPCodeContainer::generateCompute()
344 {
345     generateOMPDeclarations();
346 
347     // Generates OMP thread function
348     generateOMPCompute();
349 
350     // Generates OMP data struct
351 
352     // Generates OMP thread function call
353     generateGOMP_parallel_start();  // TODO : add parameters
354     generateDSPOMPCompute();
355     generateGOMP_parallel_end();
356 }
357 
generateComputeAux()358 BlockInst* LLVMOpenMPCodeContainer::generateComputeAux()
359 {
360     // TODO
361     return nullptr;
362 }
363 
generateOMPCompute()364 void LLVMOpenMPCodeContainer::generateOMPCompute()
365 {
366     // TODO
367 }
368 
generateDSPOMPCompute()369 void LLVMOpenMPCodeContainer::generateDSPOMPCompute()
370 {
371 }
372 
generateGOMP_parallel_start()373 void LLVMOpenMPCodeContainer::generateGOMP_parallel_start()
374 {
375 }
376 
generateGOMP_parallel_end()377 void LLVMOpenMPCodeContainer::generateGOMP_parallel_end()
378 {
379 }
380 
generateGOMP_single_start()381 LLVMValue LLVMOpenMPCodeContainer::generateGOMP_single_start()
382 {
383     return nullptr;
384 }
385 
generateGOMP_barrier()386 void LLVMOpenMPCodeContainer::generateGOMP_barrier()
387 {
388 }
389 
generateGOMP_sections_start(LLVMValue num)390 void LLVMOpenMPCodeContainer::generateGOMP_sections_start(LLVMValue num)
391 {
392 }
393 
generateGOMP_sections_end()394 void LLVMOpenMPCodeContainer::generateGOMP_sections_end()
395 {
396 }
397 
generateGOMP_sections_next()398 void LLVMOpenMPCodeContainer::generateGOMP_sections_next()
399 {
400 }
401 
generateOMPDeclarations()402 void LLVMOpenMPCodeContainer::generateOMPDeclarations()
403 {
404 }
405 
406 // Works stealing scheduler
LLVMWorkStealingCodeContainer(const string & name,int numInputs,int numOutputs)407 LLVMWorkStealingCodeContainer::LLVMWorkStealingCodeContainer(const string& name, int numInputs, int numOutputs)
408     : WSSCodeContainer(numInputs, numOutputs, "dsp"), LLVMCodeContainer(name, numInputs, numOutputs)
409 {
410 }
411 
~LLVMWorkStealingCodeContainer()412 LLVMWorkStealingCodeContainer::~LLVMWorkStealingCodeContainer()
413 {
414 }
415 
generateCompute()416 void LLVMWorkStealingCodeContainer::generateCompute()
417 {
418     // Possibly generate separated functions
419     generateComputeFunctions(fCodeProducer);
420 
421     // Generates "computeThread" code
422     generateComputeThread("computeThread" + fKlassName, "dsp", false, false)->accept(fCodeProducer);
423 
424     // Generates prototype to be used by worker threads
425     generateComputeThreadExternal("computeThreadExternal", "dsp")->accept(fCodeProducer);
426 
427     // Generates compute
428     generateComputeFun("compute" + fKlassName, "dsp", false, false)->accept(fCodeProducer);
429 }
430 
generateComputeAux()431 BlockInst* LLVMWorkStealingCodeContainer::generateComputeAux()
432 {
433     BlockInst* block = InstBuilder::genBlockInst();
434     // Control
435     block->pushBackInst(fComputeBlockInstructions);
436     return block;
437 }
438