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