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