1 /************************************************************************
2  ************************************************************************
3     FAUST compiler
4     Copyright (C) 2017 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 "rust_code_container.hh"
23 #include "Text.hh"
24 #include "exception.hh"
25 #include "fir_function_builder.hh"
26 #include "floats.hh"
27 #include "global.hh"
28 
29 using namespace std;
30 
31 /*
32  Rust backend description:
33 
34  - 'usize' type has to be used for all array access: cast index as 'usize' only when using it (load/store arrays)
35  - TODO: local stack variables (shared computation) are normally non-mutable
36  - inputN/outputN local buffer variables in 'compute' are not created at all: they are replaced directly in the code
37  with inputs[N]/outputs[N] (done in instructions_compiler.cpp)
38  - BoolOpcode BinOps always casted to integer
39  - 'delete' for SubContainers is not generated
40  - add 'kMutable' and 'kReference' address access type
41 
42 */
43 
44 map<string, bool> RustInstVisitor::gFunctionSymbolTable;
45 
produceFactory()46 dsp_factory_base* RustCodeContainer::produceFactory()
47 {
48     return new text_dsp_factory_aux(
49         fKlassName, "", "",
50         ((dynamic_cast<ostringstream*>(fOut)) ? dynamic_cast<ostringstream*>(fOut)->str() : ""), "");
51 }
52 
createScalarContainer(const string & name,int sub_container_type)53 CodeContainer* RustCodeContainer::createScalarContainer(const string& name, int sub_container_type)
54 {
55     return new RustScalarCodeContainer(name, 0, 1, fOut, sub_container_type);
56 }
57 
createContainer(const string & name,int numInputs,int numOutputs,ostream * dst)58 CodeContainer* RustCodeContainer::createContainer(const string& name, int numInputs, int numOutputs, ostream* dst)
59 {
60     gGlobal->gDSPStruct = true;
61     CodeContainer* container;
62 
63     if (gGlobal->gFloatSize == 3) {
64         throw faustexception("ERROR : quad format not supported for Rust\n");
65     }
66     if (gGlobal->gOpenCLSwitch) {
67         throw faustexception("ERROR : OpenCL not supported for Rust\n");
68     }
69     if (gGlobal->gCUDASwitch) {
70         throw faustexception("ERROR : CUDA not supported for Rust\n");
71     }
72 
73     if (gGlobal->gOpenMPSwitch) {
74         throw faustexception("ERROR : OpenMP not supported for Rust\n");
75     } else if (gGlobal->gSchedulerSwitch) {
76         throw faustexception("ERROR : Scheduler not supported for Rust\n");
77     } else if (gGlobal->gVectorSwitch) {
78         // container = new RustVectorCodeContainer(name, numInputs, numOutputs, dst);
79         throw faustexception("ERROR : Vector not supported for Rust\n");
80     } else {
81         container = new RustScalarCodeContainer(name, numInputs, numOutputs, dst, kInt);
82     }
83 
84     return container;
85 }
86 
produceInternal()87 void RustCodeContainer::produceInternal()
88 {
89     int n = 0;
90 
91     // Global declarations
92     tab(n, *fOut);
93     fCodeProducer.Tab(n);
94     generateGlobalDeclarations(&fCodeProducer);
95 
96     tab(n, *fOut);
97     *fOut << "pub struct " << fKlassName << " {";
98     tab(n + 1, *fOut);
99 
100     // Fields
101     fCodeProducer.Tab(n + 1);
102     generateDeclarations(&fCodeProducer);
103 
104     back(1, *fOut);
105     *fOut << "}";
106 
107     tab(n, *fOut);
108     tab(n, *fOut);
109     *fOut << "impl " << fKlassName << " {";
110 
111     tab(n + 1, *fOut);
112     tab(n + 1, *fOut);
113     produceInfoFunctions(n + 1, fKlassName, "&self", false, false, &fCodeProducer);
114 
115     // Init
116     // TODO
117     // generateInstanceInitFun("instanceInit" + fKlassName, false, false)->accept(&fCodeProducer);
118 
119     tab(n + 1, *fOut);
120     *fOut << "fn instance_init" << fKlassName << "(&mut self, sample_rate: i32) {";
121     tab(n + 2, *fOut);
122     fCodeProducer.Tab(n + 2);
123     generateInit(&fCodeProducer);
124     generateResetUserInterface(&fCodeProducer);
125     generateClear(&fCodeProducer);
126     back(1, *fOut);
127     *fOut << "}";
128 
129     // Fill
130     tab(n + 1, *fOut);
131     string counter = "count";
132     if (fSubContainerType == kInt) {
133         tab(n + 1, *fOut);
134         *fOut << "fn fill" << fKlassName << subst("(&mut self, $0: i32, table: &mut[i32]) {", counter);
135     } else {
136         tab(n + 1, *fOut);
137         *fOut << "fn fill" << fKlassName << subst("(&mut self, $0: i32, table: &mut[$1]) {", counter, ifloat());
138     }
139     tab(n + 2, *fOut);
140     fCodeProducer.Tab(n + 2);
141     generateComputeBlock(&fCodeProducer);
142     SimpleForLoopInst* loop = fCurLoop->generateSimpleScalarLoop(counter);
143     loop->accept(&fCodeProducer);
144     back(1, *fOut);
145     *fOut << "}" << endl;
146 
147     tab(n, *fOut);
148     *fOut << "}" << endl;
149 
150     // Memory methods
151     tab(n, *fOut);
152     tab(n, *fOut);
153     *fOut << "pub fn new" << fKlassName << "() -> " << fKlassName << " { ";
154     tab(n + 1, *fOut);
155     *fOut << fKlassName << " {";
156     RustInitFieldsVisitor initializer(fOut, n + 2);
157     generateDeclarations(&initializer);
158     tab(n + 1, *fOut);
159     *fOut << "}";
160     tab(n, *fOut);
161     *fOut << "}";
162 }
163 
produceClass()164 void RustCodeContainer::produceClass()
165 {
166     int n = 0;
167 
168     // Generate gub containers
169     generateSubContainers();
170 
171     // Functions
172     tab(n, *fOut);
173     fCodeProducer.Tab(n);
174     generateGlobalDeclarations(&fCodeProducer);
175 
176     *fOut << "pub struct " << fKlassName << " {";
177     tab(n + 1, *fOut);
178 
179     // Fields
180     fCodeProducer.Tab(n + 1);
181     generateDeclarations(&fCodeProducer);
182 
183     back(1, *fOut);
184     *fOut << "}";
185     tab(n, *fOut);
186 
187     tab(n, *fOut);
188     *fOut << "impl FaustDsp for " << fKlassName << " {";
189 
190     // Associated type
191     tab(n + 1, *fOut);
192     *fOut << "type T = " << ifloat() << ";";
193 
194     // Memory methods
195     tab(n + 2, *fOut);
196     if (fAllocateInstructions->fCode.size() > 0) {
197         tab(n + 2, *fOut);
198         *fOut << "static void allocate" << fKlassName << "(" << fKlassName << "* dsp) {";
199         tab(n + 2, *fOut);
200         fAllocateInstructions->accept(&fCodeProducer);
201         back(1, *fOut);
202         *fOut << "}";
203     }
204 
205     tab(n + 1, *fOut);
206 
207     if (fDestroyInstructions->fCode.size() > 0) {
208         tab(n + 1, *fOut);
209         *fOut << "static void destroy" << fKlassName << "(" << fKlassName << "* dsp) {";
210         tab(n + 2, *fOut);
211         fDestroyInstructions->accept(&fCodeProducer);
212          back(1, *fOut);
213         *fOut << "}";
214         tab(n + 1, *fOut);
215     }
216 
217     *fOut << "fn new() -> " << fKlassName << " { ";
218     if (fAllocateInstructions->fCode.size() > 0) {
219         tab(n + 2, *fOut);
220         *fOut << "allocate" << fKlassName << "(dsp);";
221     }
222     tab(n + 2, *fOut);
223     *fOut << fKlassName << " {";
224     RustInitFieldsVisitor initializer(fOut, n + 3);
225     generateDeclarations(&initializer);
226     tab(n + 2, *fOut);
227     *fOut << "}";
228     tab(n + 1, *fOut);
229     *fOut << "}";
230 
231     // Print metadata declaration
232     produceMetadata(n + 1);
233 
234     // Get sample rate method
235     tab(n + 1, *fOut);
236     fCodeProducer.Tab(n + 1);
237     generateGetSampleRate("get_sample_rate", "&self", false, false)->accept(&fCodeProducer);
238 
239     produceInfoFunctions(n + 1, "", "&self", false, false, &fCodeProducer);
240 
241     // Inits
242 
243     // TODO
244     //
245     // CInstVisitor codeproducer1(fOut, "");
246     // codeproducer1.Tab(n+2);
247     // generateStaticInitFun("classInit" + fKlassName, false)->accept(&codeproducer1);
248     // generateInstanceInitFun("instanceInit" + fKlassName, false, false)->accept(&codeproducer2);
249 
250     tab(n + 1, *fOut);
251     *fOut << "fn class_init(sample_rate: i32) {";
252     {
253         tab(n + 2, *fOut);
254         // Local visitor here to avoid DSP object type wrong generation
255         RustInstVisitor codeproducer(fOut, "");
256         codeproducer.Tab(n + 2);
257         generateStaticInit(&codeproducer);
258     }
259     back(1, *fOut);
260     *fOut << "}";
261 
262     tab(n + 1, *fOut);
263     *fOut << "fn instance_reset_params(&mut self) {";
264     {
265         tab(n + 2, *fOut);
266         // Local visitor here to avoid DSP object type wrong generation
267         RustInstVisitor codeproducer(fOut, "");
268         codeproducer.Tab(n + 2);
269         generateResetUserInterface(&codeproducer);
270     }
271     back(1, *fOut);
272     *fOut << "}";
273 
274     tab(n + 1, *fOut);
275     *fOut << "fn instance_clear(&mut self) {";
276     {
277         tab(n + 2, *fOut);
278         // Local visitor here to avoid DSP object type wrong generation
279         RustInstVisitor codeproducer(fOut, "");
280         codeproducer.Tab(n + 2);
281         generateClear(&codeproducer);
282     }
283     back(1, *fOut);
284     *fOut << "}";
285 
286     tab(n + 1, *fOut);
287     *fOut << "fn instance_constants(&mut self, sample_rate: i32) {";
288     {
289         tab(n + 2, *fOut);
290         // Local visitor here to avoid DSP object type wrong generation
291         RustInstVisitor codeproducer(fOut, "");
292         codeproducer.Tab(n + 2);
293         generateInit(&codeproducer);
294     }
295     back(1, *fOut);
296     *fOut << "}";
297 
298     tab(n + 1, *fOut);
299     *fOut << "fn instance_init(&mut self, sample_rate: i32) {";
300     tab(n + 2, *fOut);
301     *fOut << "self.instance_constants(sample_rate);";
302     tab(n + 2, *fOut);
303     *fOut << "self.instance_reset_params();";
304     tab(n + 2, *fOut);
305     *fOut << "self.instance_clear();";
306     tab(n + 1, *fOut);
307     *fOut << "}";
308 
309     tab(n + 1, *fOut);
310     *fOut << "fn init(&mut self, sample_rate: i32) {";
311     tab(n + 2, *fOut);
312     *fOut << fKlassName << "::class_init(sample_rate);";
313     tab(n + 2, *fOut);
314     *fOut << "self.instance_init(sample_rate);";
315     tab(n + 1, *fOut);
316     *fOut << "}";
317 
318     // Pre-pass of user interface instructions to determine parameter lookup table (field name => index)
319     UserInterfaceParameterMapping parameterMappingVisitor;
320     fUserInterfaceInstructions->accept(&parameterMappingVisitor);
321     auto parameterLookup = parameterMappingVisitor.getParameterLookup();
322 
323     // User interface (non-static method)
324     tab(n + 1, *fOut);
325     tab(n + 1, *fOut);
326     *fOut << "fn build_user_interface(&self, ui_interface: &mut dyn UI<Self::T>) {";
327     tab(n + 2, *fOut);
328     *fOut << "Self::build_user_interface_static(ui_interface);";
329     tab(n + 1, *fOut);
330     *fOut << "}";
331 
332     // User interface (static method)
333     tab(n + 1, *fOut);
334     tab(n + 1, *fOut);
335     *fOut << "fn build_user_interface_static(ui_interface: &mut dyn UI<Self::T>) {";
336     tab(n + 2, *fOut);
337     fCodeProducer.Tab(n + 2);
338     RustUIInstVisitor uiCodeproducer(fOut, "", parameterLookup, n + 2);
339     generateUserInterface(&uiCodeproducer);
340     back(1, *fOut);
341     *fOut << "}";
342 
343     // Parameter getter/setter
344     produceParameterGetterSetter(n + 1, parameterLookup);
345 
346     // Compute
347     generateCompute(n + 1);
348 
349     tab(n, *fOut);
350     *fOut << "}" << endl;
351     tab(n, *fOut);
352 
353     // Generate user interface macros if needed
354     printMacros(*fOut, n);
355 }
356 
produceMetadata(int n)357 void RustCodeContainer::produceMetadata(int n)
358 {
359     tab(n, *fOut);
360     *fOut << "fn metadata(&self, m: &mut dyn Meta) { ";
361 
362     // We do not want to accumulate metadata from all hierachical levels, so the upper level only is kept
363     for (const auto& i : gGlobal->gMetaDataSet) {
364         if (i.first != tree("author")) {
365             tab(n + 1, *fOut);
366             *fOut << "m.declare(\"" << *(i.first) << "\", " << **(i.second.begin()) << ");";
367         } else {
368             // But the "author" meta data is accumulated, the upper level becomes the main author and sub-levels become
369             // "contributor"
370             for (set<Tree>::iterator j = i.second.begin(); j != i.second.end(); j++) {
371                 if (j == i.second.begin()) {
372                     tab(n + 1, *fOut);
373                     *fOut << "m.declare(\"" << *(i.first) << "\", " << **j << ");";
374                 } else {
375                     tab(n + 1, *fOut);
376                     *fOut << "m.declare(\""
377                           << "contributor"
378                           << "\", " << **j << ");";
379                 }
380             }
381         }
382     }
383 
384     tab(n, *fOut);
385     *fOut << "}" << endl;
386 }
387 
produceInfoFunctions(int tabs,const string & classname,const string & obj,bool ismethod,bool isvirtual,TextInstVisitor * producer)388 void RustCodeContainer::produceInfoFunctions(int tabs, const string& classname, const string& obj, bool ismethod, bool isvirtual,
389                                              TextInstVisitor* producer)
390 {
391     producer->Tab(tabs);
392     generateGetInputs(subst("get_num_inputs$0", classname), obj, false, false)->accept(&fCodeProducer);
393     generateGetOutputs(subst("get_num_outputs$0", classname), obj, false, false)->accept(&fCodeProducer);
394 }
395 
produceParameterGetterSetter(int tabs,map<string,int> parameterLookup)396 void RustCodeContainer::produceParameterGetterSetter(int tabs, map<string, int> parameterLookup)
397 {
398     // Add `get_param`
399     tab(tabs, *fOut);
400     tab(tabs, *fOut);
401     *fOut << "fn get_param(&self, param: ParamIndex) -> Option<Self::T> {";
402     tab(tabs + 1, *fOut);
403     *fOut << "match param.0 {";
404     for (const auto &paramPair : parameterLookup) {
405         const auto fieldName = paramPair.first;
406         const auto index = paramPair.second;
407         tab(tabs + 2, *fOut);
408         *fOut << index << " => Some(self." << fieldName << "),";
409     }
410     tab(tabs + 2, *fOut);
411     *fOut << "_ => None,";
412     tab(tabs + 1, *fOut);
413     *fOut << "}";
414     tab(tabs, *fOut);
415     *fOut << "}";
416 
417     // Add `set_param`
418     tab(tabs, *fOut);
419     tab(tabs, *fOut);
420     *fOut << "fn set_param(&mut self, param: ParamIndex, value: Self::T) {";
421     tab(tabs + 1, *fOut);
422     *fOut << "match param.0 {";
423     for (const auto &paramPair : parameterLookup) {
424         const auto fieldName = paramPair.first;
425         const auto index = paramPair.second;
426         tab(tabs + 2, *fOut);
427         *fOut << index << " => { self." << fieldName << " = value }";
428     }
429     tab(tabs + 2, *fOut);
430     *fOut << "_ => {}";
431     tab(tabs + 1, *fOut);
432     *fOut << "}";
433     tab(tabs, *fOut);
434     *fOut << "}";
435 }
436 
437 // Scalar
RustScalarCodeContainer(const string & name,int numInputs,int numOutputs,std::ostream * out,int sub_container_type)438 RustScalarCodeContainer::RustScalarCodeContainer(const string& name, int numInputs, int numOutputs, std::ostream* out,
439                                                  int sub_container_type)
440     : RustCodeContainer(name, numInputs, numOutputs, out)
441 {
442     fSubContainerType = sub_container_type;
443 }
444 
generateCompute(int n)445 void RustScalarCodeContainer::generateCompute(int n)
446 {
447     // Generates declaration
448     tab(n, *fOut);
449     tab(n, *fOut);
450     *fOut << "fn compute("
451           << subst("&mut self, $0: i32, inputs: &[&[Self::T]], outputs: &mut[&mut[Self::T]]) {", fFullCount);
452     tab(n + 1, *fOut);
453     fCodeProducer.Tab(n + 1);
454 
455     // Generates local variables declaration and setup
456     generateComputeBlock(&fCodeProducer);
457 
458     // Generates one single scalar loop
459     std::vector<std::string> iterators;
460     for (int i = 0; i < fNumInputs; ++i) {
461         iterators.push_back("inputs" + std::to_string(i));
462     }
463     for (int i = 0; i < fNumOutputs; ++i) {
464         iterators.push_back("outputs" + std::to_string(i));
465     }
466     IteratorForLoopInst* loop = fCurLoop->generateSimpleScalarLoop(iterators);
467     loop->accept(&fCodeProducer);
468 
469     // Currently for soundfile management
470     generatePostComputeBlock(&fCodeProducer);
471 
472     back(1, *fOut);
473     *fOut << "}" << endl;
474 }
475 
476 // Vector
RustVectorCodeContainer(const string & name,int numInputs,int numOutputs,std::ostream * out)477 RustVectorCodeContainer::RustVectorCodeContainer(const string& name, int numInputs, int numOutputs, std::ostream* out)
478     : VectorCodeContainer(numInputs, numOutputs), RustCodeContainer(name, numInputs, numOutputs, out)
479 {
480 }
481 
generateCompute(int n)482 void RustVectorCodeContainer::generateCompute(int n)
483 {
484     // Possibly generate separated functions
485     fCodeProducer.Tab(n);
486     tab(n, *fOut);
487     generateComputeFunctions(&fCodeProducer);
488 
489     // Compute declaration
490     tab(n, *fOut);
491     *fOut << "fn compute("
492           << subst("&mut self, $0: i32, inputs: &[&[Self::T]], outputs: &mut[&mut[Self::T]]) {", fFullCount);
493     tab(n + 1, *fOut);
494     fCodeProducer.Tab(n + 1);
495 
496     // Generates local variables declaration and setup
497     generateComputeBlock(&fCodeProducer);
498 
499     // Generates the DSP loop
500     fDAGBlock->accept(&fCodeProducer);
501 
502     back(1, *fOut);
503     *fOut << "}" << endl;
504 }
505 
506 // OpenMP
RustOpenMPCodeContainer(const string & name,int numInputs,int numOutputs,std::ostream * out)507 RustOpenMPCodeContainer::RustOpenMPCodeContainer(const string& name, int numInputs, int numOutputs, std::ostream* out)
508     : OpenMPCodeContainer(numInputs, numOutputs), RustCodeContainer(name, numInputs, numOutputs, out)
509 {
510 }
511 
generateCompute(int n)512 void RustOpenMPCodeContainer::generateCompute(int n)
513 {
514     // Possibly generate separated functions
515     fCodeProducer.Tab(n);
516     tab(n, *fOut);
517     generateComputeFunctions(&fCodeProducer);
518 
519     // Compute declaration
520     tab(n, *fOut);
521     *fOut << "fn compute("
522           << subst("&mut self, $0: i32, inputs: &[&[Self::T]], outputs: &mut[&mut[Self::T]]) {", fFullCount);
523     tab(n + 1, *fOut);
524     fCodeProducer.Tab(n + 1);
525 
526     // Generates local variables declaration and setup
527     generateComputeBlock(&fCodeProducer);
528 
529     // Generate it
530     fGlobalLoopBlock->accept(&fCodeProducer);
531 
532     back(1, *fOut);
533     *fOut << "}" << endl;
534 }
535 
536 // Works stealing scheduler
RustWorkStealingCodeContainer(const string & name,int numInputs,int numOutputs,std::ostream * out)537 RustWorkStealingCodeContainer::RustWorkStealingCodeContainer(const string& name, int numInputs, int numOutputs,
538                                                              std::ostream* out)
539     : WSSCodeContainer(numInputs, numOutputs, "dsp"), RustCodeContainer(name, numInputs, numOutputs, out)
540 {
541 }
542 
generateCompute(int n)543 void RustWorkStealingCodeContainer::generateCompute(int n)
544 {
545     // Possibly generate separated functions
546     fCodeProducer.Tab(n);
547     tab(n, *fOut);
548     generateComputeFunctions(&fCodeProducer);
549 
550     // Generates "computeThread" code
551     // Note that users either have to adjust the trait in their architecture file.
552     // Alternatively we would have to attach this method to the impl, not the trait.
553     tab(n, *fOut);
554     *fOut << "pub fn compute_thread(" << fKlassName << "&mut self, num_thread: i32) {";
555     tab(n + 1, *fOut);
556     fCodeProducer.Tab(n + 1);
557 
558     // Generate it
559     fThreadLoopBlock->accept(&fCodeProducer);
560 
561     tab(n, *fOut);
562     *fOut << "}" << endl;
563 
564     // Compute "compute" declaration
565     tab(n, *fOut);
566     *fOut << "fn compute("
567           << subst("&mut self, $0: i32, inputs: &[&[Self::T]], outputs: &mut[&mut[Self::T]]) {", fFullCount);
568     tab(n + 1, *fOut);
569     fCodeProducer.Tab(n + 1);
570 
571     // Generates local variables declaration and setup
572     generateComputeBlock(&fCodeProducer);
573 
574     tab(n, *fOut);
575     *fOut << "}" << endl;
576 
577     tab(n, *fOut);
578     *fOut << "extern \"C\" void computeThreadExternal(&mut self, num_thread: i32) {";
579     tab(n + 1, *fOut);
580     *fOut << "compute_thread((" << fKlassName << "*)dsp, num_thread);";
581     tab(n, *fOut);
582     *fOut << "}" << endl;
583 }
584