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
29 namespace SeExpr2 {
30 
31 void 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 
69 void 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)
110 template <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 
152 namespace {
153 
155 struct 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 
189 template <char op, int d>
190 struct 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 
252 template <char op, int d>
253 struct 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 
279 template <int d>
280 struct 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 
294 template <int d>
295 struct 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 
306 template <int d>
307 struct 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 
319 struct 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 
329 struct 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 
340 struct 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 
351 struct JmpRelative {
352  static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) { return opData[0]; }
353 };
354 
356 struct 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 
369 template <int dim>
370 struct 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 
382 template <char uniform, int dim>
383 struct 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 
403 template <char op, int d>
404 struct 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 
429 template <char op>
430 struct 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 
440 template <char op, int d>
441 struct 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 
457 namespace {
458 int 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 
465 namespace {
466 int 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 
487 int ExprLocalFunctionNode::buildInterpreterForCall(const ExprFuncNode* callerNode, Interpreter* interpreter) const {
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 
538 int ExprNode::buildInterpreter(Interpreter* interpreter) const {
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 
763 void 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 
910 int 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 
931 int 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 
980 int 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 
1044 int ExprBlockNode::buildInterpreter(Interpreter* interpreter) const {
1045  assert(numChildren() == 2);
1046  child(0)->buildInterpreter(interpreter);
1047  return child(1)->buildInterpreter(interpreter);
1048 }
1049 
1050 int 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 }
SeExpr2::ExprType::isString
bool isString() const
Definition: ExprType.h:169
SeExpr2::ExprVarNode::_localVar
ExprLocalVar * _localVar
Definition: ExprNode.h:481
SeExpr2::ExprLocalFunctionNode::prototype
const ExprPrototypeNode * prototype() const
TODO: Accessor for prototype (probably not needed when we use prep right)
Definition: ExprNode.h:317
SeExpr2::Interpreter::varToLoc
VarToLoc varToLoc
Definition: Interpreter.h:51
SeExpr2::isString
const ExprStrNode * isString(const ExprNode *testee)
Definition: ExprPatterns.h:44
SeExpr2::Interpreter::eval
void eval(VarBlock *varBlock, bool debug=false)
Evaluate program.
Definition: Interpreter.cpp:31
f
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
SeExpr2::ExprUnaryOpNode::_op
char _op
Definition: ExprNode.h:402
SeExpr2::Interpreter::endOp
void endOp(bool execute=true)
Definition: Interpreter.h:83
SeExpr2::VarBlock
A thread local evaluation context. Just allocate and fill in with data.
Definition: VarBlock.h:33
SeExpr2::ExprNode::numChildren
int numChildren() const
Number of children.
Definition: ExprNode.h:114
SeExpr2::ExprNode::type
const ExprType & type() const
The type of the node.
Definition: ExprNode.h:145
SeExpr2::Interpreter::addOp
int addOp(OpF op)
! adds an operator to the program (pointing to the data at the current location)
Definition: Interpreter.h:73
SeExpr2::ExprIfThenElseNode::_varEnvMergeIndex
size_t _varEnvMergeIndex
Definition: ExprNode.h:351
SeExpr2::ExprNumNode::value
double value() const
Definition: ExprNode.h:493
SeExpr2::ExprLocalFunctionNode::buildInterpreter
int buildInterpreter(Interpreter *interpreter) const
Build the interpreter.
Definition: Interpreter.cpp:472
SeExpr2::Interpreter::d
std::vector< double > d
Double data (constants and evaluated)
Definition: Interpreter.h:43
SeExpr2::ExprUnaryOpNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:658
SeExpr2::ExprIfThenElseNode::_varEnv
ExprVarEnv * _varEnv
Definition: ExprNode.h:350
SeExpr2::ExprNode
Definition: ExprNode.h:72
SeExpr2::ExprLocalFunctionNode::_procedurePC
int _procedurePC
Definition: ExprNode.h:326
Interpreter.h
SeExpr2::ExprVarNode::var
const ExprVarRef * var() const
Definition: ExprNode.h:477
SeExpr2::Interpreter::opData
std::vector< int > opData
Ooperands to op.
Definition: Interpreter.h:47
SeExpr2::copyVarToPromotedPosition
void copyVarToPromotedPosition(Interpreter *interpreter, ExprLocalVar *varSource, ExprLocalVar *varDest)
Definition: Interpreter.cpp:763
SeExpr2::ExprLocalVarPhi
ExprLocalVar join (merge) references. Remembers which variables are possible assigners to this.
Definition: ExprEnv.h:67
SeExpr2::ExprType::dim
int dim() const
Definition: ExprType.h:160
SeExpr2::ExprBinaryOpNode::_out
char * _out
Definition: ExprNode.h:461
SeExpr2::ExprType
Definition: ExprType.h:39
SeExpr2::VarBlock::d
std::vector< double > d
copy of Interpreter's double data
Definition: VarBlock.h:68
SeExpr2::ExprFuncNode
Node that calls a function.
Definition: ExprNode.h:517
Platform.h
Platform-specific classes, functions, and includes.
SeExpr2::ExprCompareNode::_op
char _op
_op '<' less-than, 'l' less-than-eq, '>' greater-than, 'g' greater-than-eq
Definition: ExprNode.h:447
SeExpr2::ExprLocalVar::type
ExprType type() const
returns type of the variable
Definition: ExprEnv.h:51
SeExpr2::ExprNumNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:543
SeExpr2::ExprVarNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:699
SeExpr2::ExprLocalVar::_type
ExprType _type
Definition: ExprEnv.h:39
SeExpr2::ExprLocalFunctionNode::buildInterpreterForCall
int buildInterpreterForCall(const ExprFuncNode *callerNode, Interpreter *interpreter) const
Build interpreter if we are called.
Definition: Interpreter.cpp:487
SeExpr2
Definition: Context.h:22
SeExpr2::ExprIfThenElseNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:785
SeExpr2::ExprAssignNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:743
SeExpr2::ExprLocalVar::buildInterpreter
int buildInterpreter(Interpreter *interpreter) const
Allocates variable for interpreter.
Definition: Interpreter.cpp:738
ExprNode.h
SeExpr2::ExprNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:538
SeExpr2::ExprVarNode::_var
ExprVarRef * _var
Definition: ExprNode.h:482
SeExpr2::Interpreter::nextPC
int nextPC()
Return the position that the next instruction will be placed at.
Definition: Interpreter.h:70
SeExpr2::VarBlockCreator::Ref
Internally implemented var ref used by SeExpr.
Definition: VarBlock.h:87
SeExpr2::Interpreter::allocFP
int allocFP(int n)
! Allocate a floating point set of data of dimension n
Definition: Interpreter.h:104
SeExpr2::ExprCompareNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:835
SeExpr2::Interpreter::callStack
std::vector< int > callStack
Definition: Interpreter.h:57
it
you may not use this file except in compliance with the License and the following modification to it
Definition: license.txt:10
SeExpr2::ExprLocalVarPhi::valid
bool valid() const
Definition: ExprEnv.h:81
SeExpr2::ExprType::isFP
bool isFP() const
Direct is predicate checks.
Definition: ExprType.h:164
SeExpr2::VarBlock::indirectIndex
int indirectIndex
indirect index to add to pointer based data
Definition: VarBlock.h:62
SeExpr2::Interpreter::s
std::vector< char * > s
constant and evaluated pointer data
Definition: Interpreter.h:45
SeExpr2::ExprBinaryOpNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:569
a
Defined as a *alpha b *alpha< br ></div >< br > float< b > float a
Definition: userdoc.txt:174
SeExpr2::ExprBinaryOpNode::_op
char _op
Definition: ExprNode.h:460
SeExpr2::ExprSubscriptNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:684
SeExpr2::VarBlock::threadSafe
bool threadSafe
if true, interpreter's data will be copied to this instance before evaluation.
Definition: VarBlock.h:65
SeExpr2::ExprAssignNode::_localVar
ExprLocalVar * _localVar
Definition: ExprNode.h:375
SeExpr2::ExprLocalVarPhi::_thenVar
ExprLocalVar * _thenVar
Definition: ExprEnv.h:90
SeExpr2::Interpreter::OpF
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
SeExpr2::Interpreter::allocPtr
int allocPtr()
Allocate a pointer location (can be anything, but typically space for char*)
Definition: Interpreter.h:111
SeExpr2::VarBlock::s
std::vector< char * > s
copy of Interpreter's str data
Definition: VarBlock.h:71
SeExpr2::VarBlock::data
char ** data()
Raw data of the data block pointer (used by compiler)
Definition: VarBlock.h:74
SeExpr2::ExprVarRef
abstract class for implementing variable references
Definition: Expression.h:45
SeExpr2::ExprVarEnv::merge
std::vector< std::pair< std::string, ExprLocalVarPhi * > > & merge(size_t index)
Definition: ExprEnv.h:142
SeExpr2::ExprNode::child
const ExprNode * child(size_t i) const
Get 0 indexed child.
Definition: ExprNode.h:117
SeExpr2::ExprStrNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:549
SeExpr2::ExprVarRef::type
virtual ExprType type() const
returns (current) type
Definition: Expression.h:59
SeExpr2::ExprLocalFunctionNode::_returnedDataOp
int _returnedDataOp
Definition: ExprNode.h:327
SeExpr2::Interpreter::ops
std::vector< std::pair< OpF, int > > ops
Definition: Interpreter.h:56
SeExpr2::ExprFuncNode::promote
int promote(int i) const
Definition: ExprNode.h:592
SeExpr2::getTemplatizedOp2
static Interpreter::OpF getTemplatizedOp2(int i)
Return the function f encapsulated in class T for the dynamic i converted to a static d....
Definition: Interpreter.cpp:111
SeExpr2::Interpreter
Definition: Interpreter.h:40
SeExpr2::ExprLocalVar
ExprLocalVar reference, all local variables in seexpr are subclasses of this or this itself.
Definition: ExprEnv.h:37
SeExpr2::ExprStrNode::_str
std::string _str
Definition: ExprNode.h:513
SeExpr2::Interpreter::_pcStart
int _pcStart
Definition: Interpreter.h:61
b
Between a and b
Definition: userdoc.txt:180
SeExpr2::Interpreter::print
void print(int pc=-1) const
Debug by printing program.
Definition: Interpreter.cpp:69
pow
< br > pow($a, 0.5)+ $b< br >< br ></div > External variables can also be overridden by local assignment. &nbsp
SeExpr2::Interpreter::addOperand
int addOperand(int param)
! Adds an operand. Note this should be done after doing the addOp!
Definition: Interpreter.h:96
SeExpr2::ExprLocalVarPhi::_elseVar
ExprLocalVar * _elseVar
Definition: ExprEnv.h:90
VarBlock.h
SeExpr2::ExprVecNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:555