SeExpr
Interpreter.cpp
Go to the documentation of this file.
1/*
2 Copyright Disney Enterprises, Inc. All rights reserved.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License
6 and the following modification to it: Section 6 Trademarks.
7 deleted and replaced with:
8
9 6. Trademarks. This License does not grant permission to use the
10 trade names, trademarks, service marks, or product names of the
11 Licensor and its affiliates, except as required for reproducing
12 the content of the NOTICE file.
13
14 You may obtain a copy of the License at
15 http://www.apache.org/licenses/LICENSE-2.0
16*/
17#include "ExprNode.h"
18#include "Interpreter.h"
19#include "VarBlock.h"
20#include "Platform.h"
21#include <iostream>
22#include <cstdio>
23#include <algorithm>
24#if !defined(WINDOWS)
25#include <dlfcn.h>
26#endif
27
28// TODO: optimize to write to location directly on a CondNode
29namespace SeExpr2 {
30
31void Interpreter::eval(VarBlock* block, bool debug) {
32 // get pointers to the working data
33 double* fp = d.data();
34 char** str = s.data();
35
36 // if we have a VarBlock instance, we need to update the working data
37 if (block) {
38 // if the VarBlock is flagged as thread safe, copy the interpreter's data to it.
39 if (block->threadSafe == true) {
40 // copy double data
41 block->d.resize(d.size());
42 fp = block->d.data();
43 memcpy(fp, d.data(), d.size() * sizeof(double));
44
45 // copy string data
46 block->s.resize(s.size());
47 str = block->s.data();
48 memcpy(str, s.data(), s.size() * sizeof(char*));
49 }
50
51 // set the variable evaluation data
52 str[0] = reinterpret_cast<char*>(block->data());
53 str[1] = reinterpret_cast<char*>(static_cast<size_t>(block->indirectIndex));
54 }
55
56 int pc = _pcStart;
57 int end = static_cast<int>(ops.size());
58 while (pc < end) {
59 if (debug) {
60 std::cerr << "Running op at " << pc << std::endl;
61 print(pc);
62 }
63 const std::pair<OpF, int>& op = ops[pc];
64 int* opCurr = &opData[0] + op.second;
65 pc += op.first(opCurr, fp, str, callStack);
66 }
67}
68
69void Interpreter::print(int pc) const {
70 std::cerr << "---- ops ----------------------" << std::endl;
71 for (size_t i = 0; i < ops.size(); i++) {
72 const char* name = "";
73#if !defined(WINDOWS)
74 Dl_info info;
75 if (dladdr((void*)ops[i].first, &info)) name = info.dli_sname;
76#endif
77 fprintf(stderr, "%s %s %p (", pc == (int)i ? "-->" : " ", name, ops[i].first);
78 int nextGuy = (i == ops.size() - 1 ? static_cast<int>(opData.size()) : ops[i + 1].second);
79 for (int k = ops[i].second; k < nextGuy; k++) {
80 fprintf(stderr, " %d", opData[k]);
81 }
82 fprintf(stderr, ")\n");
83 }
84 std::cerr << "---- opdata ----------------------" << std::endl;
85 for (size_t k = 0; k < opData.size(); k++) {
86 std::cerr << "opData[" << k << "]= " << opData[k] << std::endl;
87 ;
88 }
89 std::cerr << "----- fp --------------------------" << std::endl;
90 for (size_t k = 0; k < d.size(); k++) {
91 std::cerr << "fp[" << k << "]= " << d[k] << std::endl;
92 ;
93 }
94 std::cerr << "---- str ----------------------" << std::endl;
95 std::cerr << "s[0] reserved for datablock = " << reinterpret_cast<size_t>(s[0]) << std::endl;
96 std::cerr << "s[1] is indirectIndex = " << reinterpret_cast<size_t>(s[1]) << std::endl;
97 for (size_t k = 2; k < s.size(); k++) {
98 std::cerr << "s[" << k << "]= 0x" << s[k];
99 if (s[k]) std::cerr << " '" << s[k][0] << s[k][1] << s[k][2] << s[k][3] << "...'";
100 std::cerr << std::endl;
101 }
102}
103
104// template Interpreter::OpF* getTemplatizedOp<Promote<1> >(int);
105// template Interpreter::OpF* getTemplatizedOp<Promote<2> >(int);
106// template Interpreter::OpF* getTemplatizedOp<Promote<3> >(int);
107
109// template using c)
110template <char c, template <char c1, int d> class T>
112 switch (i) {
113 case 1:
114 return T<c, 1>::f;
115 case 2:
116 return T<c, 2>::f;
117 case 3:
118 return T<c, 3>::f;
119 case 4:
120 return T<c, 4>::f;
121 case 5:
122 return T<c, 5>::f;
123 case 6:
124 return T<c, 6>::f;
125 case 7:
126 return T<c, 7>::f;
127 case 8:
128 return T<c, 8>::f;
129 case 9:
130 return T<c, 9>::f;
131 case 10:
132 return T<c, 10>::f;
133 case 11:
134 return T<c, 11>::f;
135 case 12:
136 return T<c, 12>::f;
137 case 13:
138 return T<c, 13>::f;
139 case 14:
140 return T<c, 14>::f;
141 case 15:
142 return T<c, 15>::f;
143 case 16:
144 return T<c, 16>::f;
145 default:
146 assert(false && "Invalid dynamic parameter (not supported template)");
147 break;
148 }
149 return 0;
150}
151
152namespace {
153
155struct BinaryStringOp {
156 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
157 // get the operand data
158 char*& out = *(char**)c[opData[0]];
159 char* in1 = c[opData[1]];
160 char* in2 = c[opData[2]];
161
162 // delete previous data and allocate a new buffer, only if needed
163 // NOTE: this is more efficient, but might consume more memory...
164 // Maybe make this behaviour configurable ?
165 int len1 = static_cast<int>(strlen(in1));
166 int len2 = static_cast<int>(strlen(in2));
167 if (out == 0 || len1 + len2 + 1 > strlen(out))
168 {
169 delete [] out;
170 out = new char [len1 + len2 + 1];
171 }
172
173 // clear previous evaluation content
174 memset(out, 0, len1 + len2 + 1);
175
176 // concatenate strings
177 strcat(out, in1);
178 strcat(out + len1, in2);
179 out[len1 + len2] = '\0';
180
181 // copy to the output
182 c[opData[3]] = out;
183
184 return 1;
185 }
186};
187
189template <char op, int d>
190struct BinaryOp {
191 static double niceMod(double a, double b) {
192 if (b == 0) return 0;
193 return a - floor(a / b) * b;
194 }
195
196 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
197 double* in1 = fp + opData[0];
198 double* in2 = fp + opData[1];
199 double* out = fp + opData[2];
200
201 for (int k = 0; k < d; k++) {
202 switch (op) {
203 case '+':
204 *out = (*in1) + (*in2);
205 break;
206 case '-':
207 *out = (*in1) - (*in2);
208 break;
209 case '*':
210 *out = (*in1) * (*in2);
211 break;
212 case '/':
213 *out = (*in1) / (*in2);
214 break;
215 case '%':
216 *out = niceMod(*in1, *in2);
217 break;
218 case '^':
219 *out = pow(*in1, *in2);
220 break;
221 // these only make sense with d==1
222 case '<':
223 *out = (*in1) < (*in2);
224 break;
225 case '>':
226 *out = (*in1) > (*in2);
227 break;
228 case 'l':
229 *out = (*in1) <= (*in2);
230 break;
231 case 'g':
232 *out = (*in1) >= (*in2);
233 break;
234 case '&':
235 *out = (*in1) && (*in2);
236 break;
237 case '|':
238 *out = (*in1) || (*in2);
239 break;
240 default:
241 assert(false);
242 }
243 in1++;
244 in2++;
245 out++;
246 }
247 return 1;
248 }
249};
250
252template <char op, int d>
253struct UnaryOp {
254 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
255 double* in = fp + opData[0];
256 double* out = fp + opData[1];
257 for (int k = 0; k < d; k++) {
258 switch (op) {
259 case '-':
260 *out = -(*in);
261 break;
262 case '~':
263 *out = 1 - (*in);
264 break;
265 case '!':
266 *out = !*in;
267 break;
268 default:
269 assert(false);
270 }
271 in++;
272 out++;
273 }
274 return 1;
275 }
276};
277
279template <int d>
280struct Subscript {
281 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
282 int tuple = opData[0];
283 int subscript = int(fp[opData[1]]);
284 int out = opData[2];
285 if (subscript >= d || subscript < 0)
286 fp[out] = 0;
287 else
288 fp[out] = fp[tuple + subscript];
289 return 1;
290 }
291};
292
294template <int d>
295struct Tuple {
296 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
297 int out = opData[d];
298 for (int k = 0; k < d; k++) {
299 fp[out + k] = fp[opData[k]];
300 }
301 return 1;
302 }
303};
304
306template <int d>
307struct AssignOp {
308 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
309 int in = opData[0];
310 int out = opData[1];
311 for (int k = 0; k < d; k++) {
312 fp[out + k] = fp[in + k];
313 }
314 return 1;
315 }
316};
317
319struct AssignStrOp {
320 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
321 int in = opData[0];
322 int out = opData[1];
323 c[out] = c[in];
324 return 1;
325 }
326};
327
329struct CondJmpRelativeIfFalse {
330 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
331 bool cond = (bool)fp[opData[0]];
332 if (!cond)
333 return opData[1];
334 else
335 return 1;
336 }
337};
338
340struct CondJmpRelativeIfTrue {
341 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
342 bool cond = (bool)fp[opData[0]];
343 if (cond)
344 return opData[1];
345 else
346 return 1;
347 }
348};
349
351struct JmpRelative {
352 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) { return opData[0]; }
353};
354
356struct EvalVar {
357 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
358 ExprVarRef* ref = reinterpret_cast<ExprVarRef*>(c[opData[0]]);
359 if (ref->type().isFP()) {
360 ref->eval(fp + opData[1]);
361 } else {
362 ref->eval(const_cast<const char**>(c + opData[1]));
363 }
364 return 1;
365 }
366};
367
369template <int dim>
370struct EvalVarBlock {
371 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
372 if (c[0]) {
373 double* basePointer = reinterpret_cast<double*>(c[0]) + opData[0];
374 double* destPointer = fp + opData[1];
375 for (int i = 0; i < dim; i++) destPointer[i] = basePointer[i];
376 }
377 return 1;
378 }
379};
380
382template <char uniform, int dim>
383struct EvalVarBlockIndirect {
384 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
385 if (c[0]) {
386 int stride = opData[2];
387 int outputVarBlockOffset = opData[0];
388 int destIndex = opData[1];
389 size_t indirectIndex = reinterpret_cast<size_t>(c[1]);
390 double* basePointer =
391 reinterpret_cast<double**>(c[0])[outputVarBlockOffset] + (uniform ? 0 : (stride * indirectIndex));
392 double* destPointer = fp + destIndex;
393 for (int i = 0; i < dim; i++) destPointer[i] = basePointer[i];
394 } else {
395 // TODO: this happens in initial evaluation!
396 // std::cerr<<"Did not get data block"<<std::endl;
397 // assert(false && "Did not get data block");
398 }
399 return 1;
400 }
401};
402
403template <char op, int d>
404struct CompareEqOp {
405 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
406 bool result = true;
407 double* in0 = fp + opData[0];
408 double* in1 = fp + opData[1];
409 double* out = fp + opData[2];
410 for (int k = 0; k < d; k++) {
411 switch (op) {
412 case '=':
413 result &= (*in0) == (*in1);
414 break;
415 case '!':
416 result &= (*in0) != (*in1);
417 break;
418 default:
419 assert(false);
420 }
421 in0++;
422 in1++;
423 }
424 *out = result;
425 return 1;
426 }
427};
428
429template <char op>
430struct CompareEqOp<op, 3> {
431 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
432 bool eq = fp[opData[0]] == fp[opData[1]] && fp[opData[0] + 1] == fp[opData[1] + 1] &&
433 fp[opData[0] + 2] == fp[opData[1] + 2];
434 if (op == '=') fp[opData[2]] = eq;
435 if (op == '!') fp[opData[2]] = !eq;
436 return 1;
437 }
438};
439
440template <char op, int d>
441struct StrCompareEqOp {
442 // TODO: this should rely on tokenization and not use strcmp
443 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
444 switch (op) {
445 case '=':
446 fp[opData[2]] = strcmp(c[opData[0]], c[opData[1]]) == 0;
447 break;
448 case '!':
449 fp[opData[2]] = strcmp(c[opData[0]], c[opData[1]]) != 0;
450 break;
451 }
452 return 1;
453 }
454};
455}
456
457namespace {
458int ProcedureReturn(int* opData, double* fp, char** c, std::vector<int>& callStack) {
459 int newPC = callStack.back();
460 callStack.pop_back();
461 return newPC - opData[0];
462}
463}
464
465namespace {
466int ProcedureCall(int* opData, double* fp, char** c, std::vector<int>& callStack) {
467 callStack.push_back(opData[0]);
468 return opData[1];
469}
470}
471
473 _procedurePC = interpreter->nextPC();
474 int lastOperand = 0;
475 for (int c = 0; c < numChildren(); c++) lastOperand = child(c)->buildInterpreter(interpreter);
476 int basePC = interpreter->nextPC();
477 ;
478 interpreter->addOp(ProcedureReturn);
479 // int endPC =
480 interpreter->addOperand(basePC);
481 interpreter->endOp(false);
482 _returnedDataOp = lastOperand;
483
484 return 0;
485}
486
488 std::vector<int> operands;
489 for (int c = 0; c < callerNode->numChildren(); c++) {
490 const ExprNode* child = callerNode->child(c);
491 // evaluate the argument
492 int operand = callerNode->child(c)->buildInterpreter(interpreter);
493 if (child->type().isFP()) {
494 if (callerNode->promote(c) != 0) {
495 // promote the argument to the needed type
496 interpreter->addOp(getTemplatizedOp<Promote>(callerNode->promote(c)));
497 // int promotedOperand=interpreter->allocFP(callerNode->promote(c));
498 interpreter->addOperand(operand);
499 interpreter->addOperand(prototype()->interpreterOps(c));
500 interpreter->endOp();
501 } else {
502 interpreter->addOp(getTemplatizedOp<AssignOp>(child->type().dim()));
503 interpreter->addOperand(operand);
504 interpreter->addOperand(prototype()->interpreterOps(c));
505 interpreter->endOp();
506 }
507 } else {
508 // TODO: string
509 assert(false);
510 }
511 operands.push_back(operand);
512 }
513 int outoperand = -1;
514 if (callerNode->type().isFP())
515 outoperand = interpreter->allocFP(callerNode->type().dim());
516 else if (callerNode->type().isString())
517 outoperand = interpreter->allocPtr();
518 else
519 assert(false);
520
521 int basePC = interpreter->nextPC();
522 interpreter->addOp(ProcedureCall);
523 int returnAddress = interpreter->addOperand(0);
524 interpreter->addOperand(_procedurePC - basePC);
525 interpreter->endOp(false);
526 // set return address
527 interpreter->opData[returnAddress] = interpreter->nextPC();
528
529 // TODO: copy result back and string
530 interpreter->addOp(getTemplatizedOp<AssignOp>(callerNode->type().dim()));
531 interpreter->addOperand(_returnedDataOp);
532 interpreter->addOperand(outoperand);
533 interpreter->endOp();
534
535 return outoperand;
536}
537
539 for (int c = 0; c < numChildren(); c++) child(c)->buildInterpreter(interpreter);
540 return -1;
541}
542
544 int loc = interpreter->allocFP(1);
545 interpreter->d[loc] = value();
546 return loc;
547}
548
550 int loc = interpreter->allocPtr();
551 interpreter->s[loc] = const_cast<char*>(_str.c_str());
552 return loc;
553}
554
556 std::vector<int> locs;
557 for (int k = 0; k < numChildren(); k++) {
558 const ExprNode* c = child(k);
559 locs.push_back(c->buildInterpreter(interpreter));
560 }
561 interpreter->addOp(getTemplatizedOp<Tuple>(numChildren()));
562 for (int k = 0; k < numChildren(); k++) interpreter->addOperand(locs[k]);
563 int loc = interpreter->allocFP(numChildren());
564 interpreter->addOperand(loc);
565 interpreter->endOp();
566 return loc;
567}
568
570 const ExprNode* child0 = child(0), *child1 = child(1);
571 int dim0 = child0->type().dim(), dim1 = child1->type().dim(), dimout = type().dim();
572 int op0 = child0->buildInterpreter(interpreter);
573 int op1 = child1->buildInterpreter(interpreter);
574 if (dimout > 1) {
575 if (dim0 != dimout) {
576 interpreter->addOp(getTemplatizedOp<Promote>(dimout));
577 int promoteOp0 = interpreter->allocFP(dimout);
578 interpreter->addOperand(op0);
579 interpreter->addOperand(promoteOp0);
580 op0 = promoteOp0;
581 interpreter->endOp();
582 }
583 if (dim1 != dimout) {
584 interpreter->addOp(getTemplatizedOp<Promote>(dimout));
585 int promoteOp1 = interpreter->allocFP(dimout);
586 interpreter->addOperand(op1);
587 interpreter->addOperand(promoteOp1);
588 op1 = promoteOp1;
589 interpreter->endOp();
590 }
591 }
592
593 // check if the node will output a string of numerical value
594 bool isString = child0->type().isString() || child1->type().isString();
595
596 // add the operator
597 if (isString == false) {
598 switch (_op) {
599 case '+':
600 interpreter->addOp(getTemplatizedOp2<'+', BinaryOp>(dimout));
601 break;
602 case '-':
603 interpreter->addOp(getTemplatizedOp2<'-', BinaryOp>(dimout));
604 break;
605 case '*':
606 interpreter->addOp(getTemplatizedOp2<'*', BinaryOp>(dimout));
607 break;
608 case '/':
609 interpreter->addOp(getTemplatizedOp2<'/', BinaryOp>(dimout));
610 break;
611 case '^':
612 interpreter->addOp(getTemplatizedOp2<'^', BinaryOp>(dimout));
613 break;
614 case '%':
615 interpreter->addOp(getTemplatizedOp2<'%', BinaryOp>(dimout));
616 break;
617 default:
618 assert(false);
619 }
620 } else {
621 switch (_op) {
622 case '+': {
623 interpreter->addOp(BinaryStringOp::f);
624 int intermediateOp = interpreter->allocPtr();
625 interpreter->s[intermediateOp] = (char*)(&_out);
626 interpreter->addOperand(intermediateOp);
627 break;
628 }
629 default:
630 assert(false);
631 }
632 }
633
634 // allocate the output
635 int op2 = -1;
636 if (isString == false) {
637 op2 = interpreter->allocFP(dimout);
638 } else {
639 op2 = interpreter->allocPtr();
640 }
641
642 interpreter->addOperand(op0);
643 interpreter->addOperand(op1);
644 interpreter->addOperand(op2);
645
646 // NOTE: one of the operand can be a function. If it's the case for
647 // strings, since functions are not immediately executed (they have
648 // endOp(false)) using endOp() here would result in a nullptr
649 // input operand during eval, thus the following arg to endOp.
650 //
651 // TODO: only stop execution if one of the operand is either a
652 // function of a var ref.
653 interpreter->endOp(isString == false);
654
655 return op2;
656}
657
659 const ExprNode* child0 = child(0);
660 int dimout = type().dim();
661 int op0 = child0->buildInterpreter(interpreter);
662
663 switch (_op) {
664 case '-':
665 interpreter->addOp(getTemplatizedOp2<'-', UnaryOp>(dimout));
666 break;
667 case '~':
668 interpreter->addOp(getTemplatizedOp2<'~', UnaryOp>(dimout));
669 break;
670 case '!':
671 interpreter->addOp(getTemplatizedOp2<'!', UnaryOp>(dimout));
672 break;
673 default:
674 assert(false);
675 }
676 int op1 = interpreter->allocFP(dimout);
677 interpreter->addOperand(op0);
678 interpreter->addOperand(op1);
679 interpreter->endOp();
680
681 return op1;
682}
683
685 const ExprNode* child0 = child(0), *child1 = child(1);
686 int dimin = child0->type().dim();
687 int op0 = child0->buildInterpreter(interpreter);
688 int op1 = child1->buildInterpreter(interpreter);
689 int op2 = interpreter->allocFP(1);
690
691 interpreter->addOp(getTemplatizedOp<Subscript>(dimin));
692 interpreter->addOperand(op0);
693 interpreter->addOperand(op1);
694 interpreter->addOperand(op2);
695 interpreter->endOp();
696 return op2;
697}
698
700 if (const ExprLocalVar* var = _localVar) {
701 // if (const ExprLocalVar* phi = var->getPhi()) var = phi;
702 Interpreter::VarToLoc::iterator i = interpreter->varToLoc.find(var);
703 if (i != interpreter->varToLoc.end())
704 return i->second;
705 else
706 throw std::runtime_error("Unallocated variable encountered.");
707 } else if (const ExprVarRef* var = _var) {
708 ExprType type = var->type();
709 int destLoc = -1;
710 if (type.isFP()) {
711 int dim = type.dim();
712 destLoc = interpreter->allocFP(dim);
713 } else
714 destLoc = interpreter->allocPtr();
715 if (const auto* blockVarRef = dynamic_cast<const VarBlockCreator::Ref*>(var)) {
716 // TODO: handle strings
717 if (blockVarRef->type().isLifetimeUniform())
718 interpreter->addOp(getTemplatizedOp2<1, EvalVarBlockIndirect>(type.dim()));
719 else
720 interpreter->addOp(getTemplatizedOp2<0, EvalVarBlockIndirect>(type.dim()));
721 interpreter->addOperand(blockVarRef->offset());
722 interpreter->addOperand(destLoc);
723 interpreter->addOperand(blockVarRef->stride());
724 interpreter->endOp();
725 } else {
726 int varRefLoc = interpreter->allocPtr();
727 interpreter->addOp(EvalVar::f);
728 interpreter->s[varRefLoc] = const_cast<char*>(reinterpret_cast<const char*>(var));
729 interpreter->addOperand(varRefLoc);
730 interpreter->addOperand(destLoc);
731 interpreter->endOp();
732 }
733 return destLoc;
734 }
735 return -1;
736}
737
739 return interpreter->varToLoc[this] =
740 _type.isFP() ? interpreter->allocFP(_type.dim()) : _type.isString() ? interpreter->allocPtr() : -1;
741}
742
744 int loc = _localVar->buildInterpreter(interpreter);
745 assert(loc != -1 && "Invalid type found");
746
747 ExprType child0Type = child(0)->type();
748 int op0 = child(0)->buildInterpreter(interpreter);
749 if (child0Type.isFP()) {
750 interpreter->addOp(getTemplatizedOp<AssignOp>(child0Type.dim()));
751 } else if (child0Type.isString()) {
752 interpreter->addOp(AssignStrOp::f);
753 } else {
754 assert(false && "Invalid desired assign type");
755 return -1;
756 }
757 interpreter->addOperand(op0);
758 interpreter->addOperand(loc);
759 interpreter->endOp(child0Type.isString() == false);
760 return loc;
761}
762
763void copyVarToPromotedPosition(Interpreter* interpreter, ExprLocalVar* varSource, ExprLocalVar* varDest) {
764 if (varDest->type().isFP()) {
765 int destDim = varDest->type().dim();
766 if (destDim != varSource->type().dim()) {
767 assert(varSource->type().dim() == 1);
768 interpreter->addOp(getTemplatizedOp<Promote>(destDim));
769 } else {
770 interpreter->addOp(getTemplatizedOp<AssignOp>(destDim));
771 }
772 interpreter->addOperand(interpreter->varToLoc[varSource]);
773 interpreter->addOperand(interpreter->varToLoc[varDest]);
774 interpreter->endOp();
775 } else if (varDest->type().isString()) {
776 interpreter->addOp(AssignStrOp::f);
777 interpreter->addOperand(interpreter->varToLoc[varSource]);
778 interpreter->addOperand(interpreter->varToLoc[varDest]);
779 interpreter->endOp();
780 } else {
781 assert(false && "failed to promote invalid type");
782 }
783}
784
786 int condop = child(0)->buildInterpreter(interpreter);
787 int basePC = interpreter->nextPC();
788
789 const auto& merges = _varEnv->merge(_varEnvMergeIndex);
790 // Allocate spots for all the join variables
791 // they are before in the sequence of operands, but it doesn't matter
792 // NOTE: at this point the variables thenVar and elseVar have not been codegen'd
793 for (auto& it : merges) {
794 ExprLocalVarPhi* finalVar = it.second;
795 if (finalVar->valid()) {
796 finalVar->buildInterpreter(interpreter);
797 }
798 }
799
800 // Setup the conditional jump
801 interpreter->addOp(CondJmpRelativeIfFalse::f);
802 interpreter->addOperand(condop);
803 int destFalse = interpreter->addOperand(0);
804 interpreter->endOp();
805
806 // Then block (build interpreter and copy variables out then jump to end)
807 child(1)->buildInterpreter(interpreter);
808 for (auto& it : merges) {
809 ExprLocalVarPhi* finalVar = it.second;
810 if (finalVar->valid()) {
811 copyVarToPromotedPosition(interpreter, finalVar->_thenVar, finalVar);
812 }
813 }
814 interpreter->addOp(JmpRelative::f);
815 int destEnd = interpreter->addOperand(0);
816 interpreter->endOp();
817
818 // Else block (build interpreter, copy variables out and then we're at end)
819 int child2PC = interpreter->nextPC();
820 child(2)->buildInterpreter(interpreter);
821 for (auto& it : merges) {
822 ExprLocalVarPhi* finalVar = it.second;
823 if (finalVar->valid()) {
824 copyVarToPromotedPosition(interpreter, finalVar->_elseVar, finalVar);
825 }
826 }
827
828 // Patch the jump addresses in the conditional
829 interpreter->opData[destFalse] = child2PC - basePC;
830 interpreter->opData[destEnd] = interpreter->nextPC() - (child2PC - 1);
831
832 return -1;
833}
834
836 const ExprNode* child0 = child(0), *child1 = child(1);
837 assert(type().dim() == 1 && type().isFP());
838
839 if (_op == '&' || _op == '|') {
840 // Handle short circuiting
841
842 // allocate output
843 int op2 = interpreter->allocFP(1);
844 // unconditionally evaluate first argument
845 int op0 = child0->buildInterpreter(interpreter);
846 // conditional to check if that argument could continue
847 int basePC = (interpreter->nextPC());
849 interpreter->addOperand(op0);
850 int destFalse = interpreter->addOperand(0);
851 interpreter->endOp();
852 // this is the no-branch case (op1=true for & and op0=false for |), so eval op1
853 int op1 = child1->buildInterpreter(interpreter);
854 // combine with &
855 interpreter->addOp(_op == '&' ? getTemplatizedOp2<'&', BinaryOp>(1) : getTemplatizedOp2<'|', BinaryOp>(1));
856 interpreter->addOperand(op0);
857 interpreter->addOperand(op1);
858 interpreter->addOperand(op2);
859 interpreter->endOp();
860 interpreter->addOp(JmpRelative::f);
861 int destEnd = interpreter->addOperand(0);
862 interpreter->endOp();
863 // this is the branch case (op1=false for & and op0=true for |) so no eval of op1 required
864 // just copy from the op0's value
865 int falseConditionPC = interpreter->nextPC();
866 interpreter->addOp(AssignOp<1>::f);
867 interpreter->addOperand(op0);
868 interpreter->addOperand(op2);
869 interpreter->endOp();
870
871 // fix PC relative jump addressses
872 interpreter->opData[destFalse] = falseConditionPC - basePC;
873 interpreter->opData[destEnd] = interpreter->nextPC() - (falseConditionPC - 1);
874
875 return op2;
876
877 } else {
878 // Noraml case, always have to evaluatee everything
879 int op0 = child0->buildInterpreter(interpreter);
880 int op1 = child1->buildInterpreter(interpreter);
881 switch (_op) {
882 case '<':
883 interpreter->addOp(getTemplatizedOp2<'<', BinaryOp>(1));
884 break;
885 case '>':
886 interpreter->addOp(getTemplatizedOp2<'>', BinaryOp>(1));
887 break;
888 case 'l':
889 interpreter->addOp(getTemplatizedOp2<'l', BinaryOp>(1));
890 break;
891 case 'g':
892 interpreter->addOp(getTemplatizedOp2<'g', BinaryOp>(1));
893 break;
894 case '&':
895 assert(false); // interpreter->addOp(getTemplatizedOp2<'&',BinaryOp>(1));break;
896 case '|':
897 assert(false); // interpreter->addOp(getTemplatizedOp2<'|',BinaryOp>(1));break;
898 default:
899 assert(false);
900 }
901 int op2 = interpreter->allocFP(1);
902 interpreter->addOperand(op0);
903 interpreter->addOperand(op1);
904 interpreter->addOperand(op2);
905 interpreter->endOp();
906 return op2;
907 }
908}
909
910int ExprPrototypeNode::buildInterpreter(Interpreter* interpreter) const {
911 // set up parents
912 _interpreterOps.clear();
913 for (int c = 0; c < numChildren(); c++) {
914 if (const ExprVarNode* childVarNode = dynamic_cast<const ExprVarNode*>(child(c))) {
915 ExprType childType = childVarNode->type();
916 if (childType.isFP()) {
917 int operand = interpreter->allocFP(childType.dim());
918 _interpreterOps.push_back(operand);
919 interpreter->varToLoc[childVarNode->localVar()] = operand;
920 }
921 } else {
922 assert(false);
923 }
924 child(c)->buildInterpreter(interpreter);
925
926 // make sure we have a slot in our global activation record for the variables!
927 }
928 return 0;
929}
930
931int ExprCompareEqNode::buildInterpreter(Interpreter* interpreter) const {
932 const ExprNode* child0 = child(0), *child1 = child(1);
933 int op0 = child0->buildInterpreter(interpreter);
934 int op1 = child1->buildInterpreter(interpreter);
935
936 if (child0->type().isFP()) {
937 int dim0 = child0->type().dim(), dim1 = child1->type().dim();
938 int dimCompare = std::max(dim0, dim1);
939 if (dimCompare > 1) {
940 if (dim0 == 1) {
941 interpreter->addOp(getTemplatizedOp<Promote>(dim1));
942 int promotedOp0 = interpreter->allocFP(dim1);
943 interpreter->addOperand(op0);
944 interpreter->addOperand(promotedOp0);
945 interpreter->endOp();
946 op0 = promotedOp0;
947 }
948 if (dim1 == 1) {
949 interpreter->addOp(getTemplatizedOp<Promote>(dim0));
950 int promotedOp1 = interpreter->allocFP(dim0);
951 interpreter->addOperand(op1);
952 interpreter->addOperand(promotedOp1);
953 interpreter->endOp();
954 op1 = promotedOp1;
955 }
956 }
957 if (_op == '=')
958 interpreter->addOp(getTemplatizedOp2<'=', CompareEqOp>(dimCompare));
959 else if (_op == '!')
960 interpreter->addOp(getTemplatizedOp2<'!', CompareEqOp>(dimCompare));
961 else
962 assert(false && "Invalid operation");
963 } else if (child0->type().isString()) {
964 if (_op == '=')
965 interpreter->addOp(getTemplatizedOp2<'=', StrCompareEqOp>(1));
966 else if (_op == '!')
967 interpreter->addOp(getTemplatizedOp2<'!', StrCompareEqOp>(1));
968 else
969 assert(false && "Invalid operation");
970 } else
971 assert(false && "Invalid type for comparison");
972 int op2 = interpreter->allocFP(1);
973 interpreter->addOperand(op0);
974 interpreter->addOperand(op1);
975 interpreter->addOperand(op2);
976 interpreter->endOp(child0->type().isString() == false);
977 return op2;
978}
979
980int ExprCondNode::buildInterpreter(Interpreter* interpreter) const {
981 int opOut = -1;
982 // TODO: handle strings!
983 int dimout = type().dim();
984
985 // conditional
986 int condOp = child(0)->buildInterpreter(interpreter);
987 int basePC = (interpreter->nextPC());
988 interpreter->addOp(CondJmpRelativeIfFalse::f);
989 interpreter->addOperand(condOp);
990 int destFalse = interpreter->addOperand(0);
991 interpreter->endOp();
992
993 // true way of working
994 int op1 = child(1)->buildInterpreter(interpreter);
995 if (type().isFP())
996 interpreter->addOp(getTemplatizedOp<AssignOp>(dimout));
997 else if (type().isString())
998 interpreter->addOp(AssignStrOp::f);
999 else
1000 assert(false);
1001 interpreter->addOperand(op1);
1002 int dataOutTrue = interpreter->addOperand(-1);
1003 interpreter->endOp(false);
1004
1005 // jump past false way of working
1006 interpreter->addOp(JmpRelative::f);
1007 int destEnd = interpreter->addOperand(0);
1008 interpreter->endOp();
1009
1010 // record start of false condition
1011 int child2PC = interpreter->nextPC();
1012
1013 // false way of working
1014 int op2 = child(2)->buildInterpreter(interpreter);
1015 if (type().isFP())
1016 interpreter->addOp(getTemplatizedOp<AssignOp>(dimout));
1017 else if (type().isString())
1018 interpreter->addOp(AssignStrOp::f);
1019 else
1020 assert(false);
1021 interpreter->addOperand(op2);
1022 int dataOutFalse = interpreter->addOperand(-1);
1023 interpreter->endOp(false);
1024
1025 // patch up relative jumps
1026 interpreter->opData[destFalse] = child2PC - basePC;
1027 interpreter->opData[destEnd] = interpreter->nextPC() - (child2PC - 1);
1028
1029 // allocate output
1030 if (type().isFP())
1031 opOut = interpreter->allocFP(type().dim());
1032 else if (type().isString())
1033 opOut = interpreter->allocPtr();
1034 else
1035 assert(false);
1036
1037 // patch outputs on assigns in each condition
1038 interpreter->opData[dataOutTrue] = opOut;
1039 interpreter->opData[dataOutFalse] = opOut;
1040
1041 return opOut;
1042}
1043
1044int ExprBlockNode::buildInterpreter(Interpreter* interpreter) const {
1045 assert(numChildren() == 2);
1046 child(0)->buildInterpreter(interpreter);
1047 return child(1)->buildInterpreter(interpreter);
1048}
1049
1050int ExprModuleNode::buildInterpreter(Interpreter* interpreter) const {
1051 int lastIdx = 0;
1052 for (int c = 0; c < numChildren(); c++) {
1053 if (c == numChildren() - 1) interpreter->setPCStart(interpreter->nextPC());
1054 lastIdx = child(c)->buildInterpreter(interpreter);
1055 }
1056 return lastIdx;
1057}
1058}
Platform-specific classes, functions, and includes.
ExprLocalVar * _localVar
Definition: ExprNode.h:375
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
char _op
_op '<' less-than, 'l' less-than-eq, '>' greater-than, 'g' greater-than-eq
Definition: ExprNode.h:447
Node that calls a function.
Definition: ExprNode.h:517
int promote(int i) const
Definition: ExprNode.h:592
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
const ExprPrototypeNode * prototype() const
TODO: Accessor for prototype (probably not needed when we use prep right)
Definition: ExprNode.h:317
int buildInterpreter(Interpreter *interpreter) const
Build the interpreter.
int buildInterpreterForCall(const ExprFuncNode *callerNode, Interpreter *interpreter) const
Build interpreter if we are called.
ExprLocalVar join (merge) references. Remembers which variables are possible assigners to this.
Definition: ExprEnv.h:67
ExprLocalVar * _elseVar
Definition: ExprEnv.h:90
bool valid() const
Definition: ExprEnv.h:81
ExprLocalVar * _thenVar
Definition: ExprEnv.h:90
ExprLocalVar reference, all local variables in seexpr are subclasses of this or this itself.
Definition: ExprEnv.h:37
ExprType type() const
returns type of the variable
Definition: ExprEnv.h:51
int buildInterpreter(Interpreter *interpreter) const
Allocates variable for interpreter.
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
int numChildren() const
Number of children.
Definition: ExprNode.h:114
const ExprNode * child(size_t i) const
Get 0 indexed child.
Definition: ExprNode.h:117
const ExprType & type() const
The type of the node.
Definition: ExprNode.h:145
double value() const
Definition: ExprNode.h:493
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
std::string _str
Definition: ExprNode.h:513
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
bool isString() const
Definition: ExprType.h:169
bool isFP() const
Direct is predicate checks.
Definition: ExprType.h:164
int dim() const
Definition: ExprType.h:160
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
std::vector< std::pair< std::string, ExprLocalVarPhi * > > & merge(size_t index)
Definition: ExprEnv.h:142
ExprLocalVar * _localVar
Definition: ExprNode.h:481
const ExprVarRef * var() const
Definition: ExprNode.h:477
ExprVarRef * _var
Definition: ExprNode.h:482
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
abstract class for implementing variable references
Definition: Expression.h:45
virtual ExprType type() const
returns (current) type
Definition: Expression.h:59
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
int allocFP(int n)
! Allocate a floating point set of data of dimension n
Definition: Interpreter.h:104
int addOp(OpF op)
! adds an operator to the program (pointing to the data at the current location)
Definition: Interpreter.h:73
int(* OpF)(int *, double *, char **, std::vector< int > &)
Op function pointer arguments are (int* currOpData,double* currD,char** c,std::stack<int>& callStacku...
Definition: Interpreter.h:54
std::vector< std::pair< OpF, int > > ops
Definition: Interpreter.h:56
void eval(VarBlock *varBlock, bool debug=false)
Evaluate program.
Definition: Interpreter.cpp:31
int addOperand(int param)
! Adds an operand. Note this should be done after doing the addOp!
Definition: Interpreter.h:96
int allocPtr()
Allocate a pointer location (can be anything, but typically space for char*)
Definition: Interpreter.h:111
int nextPC()
Return the position that the next instruction will be placed at.
Definition: Interpreter.h:70
void endOp(bool execute=true)
Definition: Interpreter.h:83
std::vector< int > opData
Ooperands to op.
Definition: Interpreter.h:47
std::vector< double > d
Double data (constants and evaluated)
Definition: Interpreter.h:43
std::vector< char * > s
constant and evaluated pointer data
Definition: Interpreter.h:45
void print(int pc=-1) const
Debug by printing program.
Definition: Interpreter.cpp:69
std::vector< int > callStack
Definition: Interpreter.h:57
Internally implemented var ref used by SeExpr.
Definition: VarBlock.h:87
A thread local evaluation context. Just allocate and fill in with data.
Definition: VarBlock.h:33
std::vector< double > d
copy of Interpreter's double data
Definition: VarBlock.h:68
int indirectIndex
indirect index to add to pointer based data
Definition: VarBlock.h:62
char ** data()
Raw data of the data block pointer (used by compiler)
Definition: VarBlock.h:74
bool threadSafe
if true, interpreter's data will be copied to this instance before evaluation.
Definition: VarBlock.h:65
std::vector< char * > s
copy of Interpreter's str data
Definition: VarBlock.h:71
you may not use this file except in compliance with the License and the following modification to it
Definition: license.txt:10
const ExprStrNode * isString(const ExprNode *testee)
Definition: ExprPatterns.h:44
void copyVarToPromotedPosition(Interpreter *interpreter, ExprLocalVar *varSource, ExprLocalVar *varDest)
static Interpreter::OpF getTemplatizedOp2(int i)
Return the function f encapsulated in class T for the dynamic i converted to a static d....
Between a and b
Definition: userdoc.txt:180
< br > pow($a, 0.5)+ $b< br >< br ></div > External variables can also be overridden by local assignment. &nbsp
Defined as a *alpha b *alpha< br ></div >< br > float< b > float a
Definition: userdoc.txt:174
with numParticles numAttributes A variable block contains variable names and types but doesn t care what the values are< pre > void f(const std::string &s, MyParticleData *p, int outputDim=3)
Definition: varblocks.txt:35