Back to index

wims  3.65+svn20090927
CompiledFunction.java
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007-2008 Mihai Preda.
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *      http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 package org.javia.arity;
00018 
00019 import java.util.Random;
00020 
00021 class CompiledFunction extends Function {
00022     //static final int INI_STACK_SIZE =  16;
00023     static final int MAX_STACK_SIZE = 128; //if stack ever grows above this likely something is wrong
00024     private static Random random = new Random();
00025 
00026     private double consts[];
00027     private Function funcs[];
00028     private byte code[];
00029     private int arity; // >= 0
00030 
00031     private static final ThreadLocal stacks = new ThreadLocal();
00032 
00033     CompiledFunction(int arity, byte[] code, double[] consts, Function funcs[]) {
00034         init(arity, code, consts, funcs);
00035     }
00036 
00037     /* This empty constructor is used only by SimpleCodeGen,
00038        which calls init() explicitly later.
00039     */
00040     CompiledFunction() {
00041         init(0, null, null, null);
00042     }
00043 
00044     void init(int arity, byte[] code, double[] consts, Function funcs[]) {
00045         this.arity  = arity;
00046         this.code   = code;
00047         this.consts = consts;
00048         this.funcs  = funcs;
00049     }
00050 
00051     public int arity() {
00052         return arity;
00053     }
00054 
00055     public String toString() {
00056         StringBuffer buf = new StringBuffer();
00057         int cpos = 0, fpos = 0;
00058         if (arity != 0) {
00059             buf.append("arity ").append(arity).append("; ");
00060         }
00061         for (int i = 0; i < code.length; ++i) {
00062             byte op = code[i];
00063             buf.append(VM.opcodeName[op]);
00064             if (op == VM.CONST) {
00065                 buf.append(' ').append(consts[cpos++]);
00066             } else if (op == VM.CALL) {
00067                 ++fpos;
00068                 //buf.append(" {").append(funcs[fpos++].toString()).append('}');
00069             }
00070             buf.append("; ");
00071         }
00072         if (cpos != consts.length) {
00073             buf.append("\nuses only ").append(cpos).append(" consts out of ").append(consts.length);
00074         }
00075         if (fpos != funcs.length) {
00076             buf.append("\nuses only ").append(fpos).append(" funcs out of ").append(funcs.length);
00077         }
00078         return buf.toString();
00079     }
00080 
00081     private void checkArity(int nArgs) throws ArityException {
00082         if (arity() != nArgs) {
00083             throw new ArityException("Expected " + arity() + " arguments, got " + nArgs);
00084         }        
00085     }
00086 
00087     private double[] getStack() {
00088         double stack[];
00089         if ((stack = (double[]) stacks.get()) == null) {
00090             stacks.set(stack = new double[MAX_STACK_SIZE]);
00091         }
00092         return stack;
00093     }
00094     
00095     static final double NO_ARGS[] = new double[0];
00096     public double eval() throws ArityException {
00097         return eval(NO_ARGS);
00098     }
00099 
00100     public double eval(double x) throws ArityException {
00101         checkArity(1);
00102         double stack[] = getStack();
00103         stack[0] = x;
00104         exec(stack, 0);
00105         return stack[0];
00106     }
00107 
00108     public double eval(double x, double y) throws ArityException {
00109         checkArity(2);
00110         double stack[] = getStack();
00111         stack[0] = x;
00112         stack[1] = y;
00113         exec(stack, 1);
00114         return stack[0];
00115     }
00116 
00117     public double eval(double args[]) throws ArityException {
00118         checkArity(args.length);
00119         double stack[] = getStack();
00120         if (args.length > 0) {
00121             System.arraycopy(args, 0, stack, 0, args.length);
00122         }
00123         exec(stack, args.length - 1);
00124         return stack[0];
00125     }
00126 
00127     void exec(double s[], int p) {
00128         int finalP = execWithoutCheck(s, p);
00129         int expected = p - arity + 1;
00130         if (finalP != expected) {
00131             throw new Error("stack pointer after exec: expected " + 
00132                             expected + ", got " + finalP);
00133         }
00134     }
00135     
00136     int execWithoutCheck(double s[], int p) {
00137         byte[] code = this.code;
00138         int initialSP = p;
00139         int constp = 0;
00140         int funp   = 0;
00141         final double angleFactor = 1; // 1/Calc.cfg.trigFactor;
00142         
00143         // arguments, read from stack on exec entry
00144         // we don't use an array in order to avoid the dynamic allocation (new)
00145         // @see Compiler.MAX_ARITY
00146         double a0, a1, a2, a3, a4;
00147         a0 = a1 = a2 = a3 = a4 = Double.NaN;
00148         switch (arity) {
00149         case 5: a4 = s[p--];
00150         case 4: a3 = s[p--];
00151         case 3: a2 = s[p--];
00152         case 2: a1 = s[p--];
00153         case 1: a0 = s[p--];
00154         }
00155 
00156         //int expectedExitSP = p+1;
00157         int codeLen = code.length;
00158         for (int pc = 0; pc < codeLen; ++pc) {
00159             switch (code[pc]) {
00160             case VM.CONST: s[++p] = consts[constp++]; break;
00161             case VM.CALL: { 
00162                 Function f = funcs[funp++];
00163                 if (f instanceof CompiledFunction) { 
00164                     p = ((CompiledFunction) f).execWithoutCheck(s, p);
00165                 } else {
00166                     int arity = f.arity();
00167                     p -= arity;
00168                     try {
00169                         double result;
00170                         switch (arity) {
00171                         case 0:
00172                             result = f.eval();
00173                             break;
00174                         case 1:
00175                             result = f.eval(s[p+1]);
00176                             break;
00177                         case 2:
00178                             result = f.eval(s[p+1], s[p+2]);
00179                             break;
00180                         default:
00181                             double args[] = new double[arity];
00182                             System.arraycopy(s, p+1, args, 0, arity);
00183                             result = f.eval(args);
00184                         }
00185                         s[++p] = result;                                      
00186                     } catch (ArityException e) {
00187                         throw new Error(""+e); //never
00188                     }
00189                 }
00190                 break;
00191             }
00192                 
00193             case VM.RND: s[++p] = random.nextDouble(); break;
00194                     
00195             case VM.ADD: s[--p] += s[p+1]; break;
00196             case VM.SUB: s[--p] -= s[p+1]; break;
00197             case VM.MUL: s[--p] *= s[p+1]; break;
00198             case VM.DIV: s[--p] /= s[p+1]; break;
00199             case VM.MOD: s[--p] %= s[p+1]; break;
00200             case VM.POWER: s[--p] = MoreMath.pow(s[p], s[p+1]); break;
00201                 
00202             case VM.UMIN: s[p] = -s[p]; break;
00203             case VM.FACT: s[p] = MoreMath.factorial(s[p]); break;                   
00204                 
00205             case VM.SIN:  s[p] = Math.sin(s[p] * angleFactor); break;
00206             case VM.COS:  s[p] = Math.cos(s[p] * angleFactor); break;
00207             case VM.TAN:  s[p] = Math.tan(s[p] * angleFactor); break;
00208             case VM.ASIN: s[p] = MoreMath.asin(s[p]) / angleFactor; break;
00209             case VM.ACOS: s[p] = MoreMath.acos(s[p]) / angleFactor; break;
00210             case VM.ATAN: s[p] = MoreMath.atan(s[p]) / angleFactor; break;
00211                     
00212             case VM.EXP: s[p] = MoreMath.exp(s[p]); break;
00213             case VM.LOG: s[p] = MoreMath.log(s[p]); break;
00214                 
00215             case VM.SQRT: s[p] = Math.sqrt(s[p]); break;
00216             case VM.CBRT: s[p] = MoreMath.cbrt(s[p]); break;
00217                 
00218             case VM.SINH: s[p] = MoreMath.sinh(s[p]); break;
00219             case VM.COSH: s[p] = MoreMath.cosh(s[p]); break;
00220             case VM.TANH: s[p] = MoreMath.tanh(s[p]); break;
00221             case VM.ASINH: s[p] = MoreMath.asinh(s[p]); break;
00222             case VM.ACOSH: s[p] = MoreMath.acosh(s[p]); break;
00223             case VM.ATANH: s[p] = MoreMath.atanh(s[p]); break;
00224                     
00225             case VM.ABS:   s[p] = Math.abs(s[p]); break;
00226             case VM.FLOOR: s[p] = Math.floor(s[p]); break;
00227             case VM.CEIL:  s[p] = Math.ceil(s[p]); break;
00228             case VM.SIGN:  s[p] = s[p] > 0 ? 1 : s[p] < 0 ? -1 : 0; break;
00229                 
00230             case VM.MIN:  s[--p] = Math.min(s[p], s[p+1]); break;
00231             case VM.MAX:  s[--p] = Math.min(s[p], s[p+1]); break;
00232             case VM.GCD:  s[--p] = MoreMath.gcd(s[p], s[p+1]); break;
00233             case VM.COMB: s[--p] = MoreMath.comb(s[p], s[p+1]); break;
00234             case VM.PERM: s[--p] = MoreMath.perm(s[p], s[p+1]); break;
00235                     
00236             case VM.LOAD0: s[++p] = a0; break;
00237             case VM.LOAD1: s[++p] = a1; break;
00238             case VM.LOAD2: s[++p] = a2; break;
00239             case VM.LOAD3: s[++p] = a3; break;
00240             case VM.LOAD4: s[++p] = a4; break;
00241             default:
00242                 throw new Error("Unknown opcode " + code[pc]);
00243             }
00244         }
00245         return p;
00246         /*
00247         if (p != expectedExitSP) {
00248             throw new Error("stack pointer after exec: expected " + expectedExitSP +
00249                             " , got " + p);
00250         }
00251         */
00252     }
00253 }