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(¶meterMappingVisitor);
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 ¶mPair : 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 ¶mPair : 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