1 /*
   2  * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.scenario.effect.compiler.backend.sw.me;
  27 
  28 import java.util.HashSet;
  29 import java.util.List;
  30 import java.util.Set;
  31 import com.sun.scenario.effect.compiler.model.BaseType;
  32 import com.sun.scenario.effect.compiler.model.FuncImpl;
  33 import com.sun.scenario.effect.compiler.model.Function;
  34 import com.sun.scenario.effect.compiler.model.Param;
  35 import com.sun.scenario.effect.compiler.model.Type;
  36 import com.sun.scenario.effect.compiler.model.Variable;
  37 import com.sun.scenario.effect.compiler.tree.ArrayAccessExpr;
  38 import com.sun.scenario.effect.compiler.tree.BinaryExpr;
  39 import com.sun.scenario.effect.compiler.tree.CallExpr;
  40 import com.sun.scenario.effect.compiler.tree.Expr;
  41 import com.sun.scenario.effect.compiler.tree.FieldSelectExpr;
  42 import com.sun.scenario.effect.compiler.tree.LiteralExpr;
  43 import com.sun.scenario.effect.compiler.tree.ParenExpr;
  44 import com.sun.scenario.effect.compiler.tree.TreeScanner;
  45 import com.sun.scenario.effect.compiler.tree.UnaryExpr;
  46 import com.sun.scenario.effect.compiler.tree.VariableExpr;
  47 import com.sun.scenario.effect.compiler.tree.VectorCtorExpr;
  48 
  49 import static com.sun.scenario.effect.compiler.backend.sw.me.MEBackend.*;
  50 
  51 /*
  52  * How should we translate function calls?  For now we will inline
  53  * everything, i.e. expand function bodies directly into the loop body.
  54  *
  55  * The approach... For an ExprStmt or VarDecl (with initializer),
  56  * walk down the tree and see if there are any function calls.
  57  * For each function call, inline the function implementation prior
  58  * to the statement output.  The statement will then refer to
  59  * the output variables from the inlined function, rather than
  60  * the function call itself.
  61  *
  62  * First declare the result variables using the name of the
  63  * called function and a field suffix, if needed; for example:
  64  *     float3 val = sample(...).rgb;
  65  * ==>
  66  *     float sample_res_r, sample_res_g, sample_res_b;
  67  *
  68  * Inside the inlined function, assign parameter expressions to
  69  * temporary variables, using the name of the declared parameters
  70  * as a guide; for example:
  71  *     float val = min(foo+0.25, 1.0);
  72  * ==>
  73  *     float min_res;
  74  *     {
  75  *         float a_tmp = foo + 0.25f;
  76  *         float b_tmp = 1.0f;
  77  *         min_res = (a_tmp < b_tmp) a_tmp : b_tmp;
  78  *     }
  79  *
  80  * In a future version, references to scalar variables and literals
  81  * could easily be inlined; for example:
  82  *     float val = min(foo+0.25*bar, 1.0);
  83  * ==>
  84  *     float min_res;
  85  *     {
  86  *         float a_tmp = foo + 0.25f * bar;
  87  *         min_res = (a_tmp < 1.0f) a_tmp : 1.0f;
  88  *     }
  89  *
  90  * Note that this system will likely produce less-than-efficient
  91  * Java code in many cases; for now we're just trying to get things
  92  * functional, and performance improvements will certainly come later.
  93  *
  94  *
  95  * Example #1:
  96  *     float3 val = scale * sample(baseImg, pos + off.xy).rgb;
  97  * ==>
  98  *     float sample_res_r, sample_res_g, sample_res_b;
  99  *     {
 100  *         float pos_x_tmp = pos_x + off_x;
 101  *         float pos_y_tmp = pos_y + off_y;
 102  *         int baseImg_tmp =
 103  *             baseImg[(int)(pos_y_tmp*srch*srcscan) + (int)(pos_x_tmp*srcw)];
 104  *         sample_res_r = (((baseImg_tmp >>  16) & 0xff) / 255f);
 105  *         sample_res_g = (((baseImg_tmp >>   8) & 0xff) / 255f);
 106  *         sample_res_b = (((baseImg_tmp       ) & 0xff) / 255f);
 107  *     }
 108  *     float val_r = scale * sample_res_r;
 109  *     float val_g = scale * sample_res_g;
 110  *     float val_b = scale * sample_res_b;
 111  *
 112  * Example #2:
 113  *     float val = scale * clamp(foo, 0.0, 1.0);
 114  * ==>
 115  *     float clamp_res;
 116  *     {
 117  *         float val_tmp = foo;
 118  *         float min_tmp = 0.0f;
 119  *         float max_tmp = 1.0f;
 120  *         if (val_tmp < min_tmp) clamp_res = min_tmp;
 121  *         else if (val_tmp > max_tmp) clamp_res = max_tmp;
 122  *         else clamp_res = val_tmp;
 123  *     }
 124  *     float val = scale * clamp_res;
 125  */
 126 class MECallScanner extends TreeScanner {
 127     private StringBuilder sb;
 128     private boolean inCallExpr = false;
 129     private Set<Integer> selectedFields = null;
 130     private boolean inFieldSelect = false;
 131     private char selectedField = 'x';
 132     private boolean inVectorOp = false;
 133     private int vectorIndex = 0;
 134 
 135     private void output(String s) {
 136         if (sb == null) {
 137             sb = new StringBuilder();
 138         }
 139         sb.append(s);
 140     }
 141 
 142     String getResult() {
 143         return (sb != null) ? sb.toString() : null;
 144     }
 145 
 146     @Override
 147     public void visitCallExpr(CallExpr e) {
 148         if (inCallExpr) {
 149             throw new InternalError("Nested function calls not yet supported");
 150         }
 151 
 152         Function func = e.getFunction();
 153         Type t = func.getReturnType();
 154         String vtype = t.getBaseType().toString();
 155         String vname = func.getName();
 156         Set<Integer> fields = selectedFields;
 157         if (t.isVector()) {
 158             if (fields == null) {
 159                 fields = new HashSet<Integer>();
 160                 for (int i = 0; i < t.getNumFields(); i++) {
 161                     fields.add(i);
 162                 }
 163             }
 164         }
 165         if (!MEBackend.isResultVarDeclared(vname)) {
 166             // only declare result variables if they haven't been already
 167             // TODO: there's a bug here; suppose a function like
 168             // min(float,float) is inlined, then later we inline
 169             // min(float3,float3), the second time we'll think we already have
 170             // declared the result variables, but min_res_y/z won't be there...
 171             MEBackend.declareResultVar(vname);
 172             if (t.isVector()) {
 173                 output(vtype + " ");
 174                 boolean first = true;
 175                 for (Integer f : fields) {
 176                     if (first) {
 177                         first = false;
 178                     } else {
 179                         output(", ");
 180                     }
 181                     output(vname + "_res" + getSuffix(f));
 182                 }
 183                 output(";\n");
 184             } else {
 185                 output(vtype + " " + vname + "_res;\n");
 186             }
 187         }
 188 
 189         inCallExpr = true;
 190         output("{\n");
 191         List<Param> params = func.getParams();
 192         List<Expr> argExprs = e.getParams();
 193         for (int i = 0; i < params.size(); i++) {
 194             Param param = params.get(i);
 195             String pname = param.getName();
 196             Type ptype = param.getType();
 197             BaseType pbasetype = ptype.getBaseType();
 198             if (pbasetype == BaseType.SAMPLER) {
 199                 // skip these for now
 200                 continue;
 201             }
 202             if (ptype.isVector()) {
 203                 inVectorOp = true;
 204                 for (int j = 0; j < ptype.getNumFields(); j++) {
 205                     vectorIndex = j;
 206                     output(pbasetype.toString());
 207                     output(" ");
 208                     output(pname + "_tmp" + getSuffix(j) + " = ");
 209                     scan(argExprs.get(i));
 210                     output(";\n");
 211                 }
 212                 inVectorOp = false;
 213             } else {
 214                 output(pbasetype.toString());
 215                 output(" ");
 216                 output(pname + "_tmp = ");
 217                 scan(argExprs.get(i));
 218                 output(";\n");
 219             }
 220         }
 221 
 222         FuncImpl impl = MEFuncImpls.get(func);
 223         if (impl != null) {
 224             // core (built-in) function
 225             String preamble = impl.getPreamble(argExprs);
 226             if (preamble != null) {
 227                 output(preamble);
 228             }
 229 
 230             if (t.isVector()) {
 231                 for (Integer f : fields) {
 232                     output(vname + "_res" + getSuffix(f) + " = ");
 233                     output(impl.toString(f, argExprs));
 234                     output(";\n");
 235                 }
 236             } else {
 237                 output(vname + "_res = ");
 238                 output(impl.toString(0, argExprs));
 239                 output(";\n");
 240             }
 241         } else {
 242             // user-defined function
 243             METreeScanner scanner = new METreeScanner(func.getName());
 244             scanner.scan(MEBackend.getFuncDef(func.getName()).getStmt());
 245             output(scanner.getResult());
 246         }
 247 
 248         output("\n}\n");
 249         inCallExpr = false;
 250     }
 251 
 252     @Override
 253     public void visitArrayAccessExpr(ArrayAccessExpr e) {
 254         if (inCallExpr) {
 255             if (e.getExpr() instanceof VariableExpr &&
 256                 e.getIndex() instanceof VariableExpr)
 257             {
 258                 VariableExpr ve = (VariableExpr)e.getExpr();
 259                 VariableExpr ie = (VariableExpr)e.getIndex();
 260                 output(ve.getVariable().getName());
 261                 output("_arr[" + ie.getVariable().getName());
 262                 output(" * " + ve.getVariable().getType().getNumFields());
 263                 output(" + " + getFieldIndex(selectedField) + "]");
 264             } else {
 265                 throw new InternalError("Array access only supports variable expr/index (for now)");
 266             }
 267         } else {
 268             super.visitArrayAccessExpr(e);
 269         }
 270     }
 271 
 272     @Override
 273     public void visitBinaryExpr(BinaryExpr e) {
 274         if (inCallExpr) {
 275             scan(e.getLeft());
 276             output(" " + e.getOp() + " ");
 277             scan(e.getRight());
 278         } else {
 279             super.visitBinaryExpr(e);
 280         }
 281     }
 282 
 283     @Override
 284     public void visitFieldSelectExpr(FieldSelectExpr e) {
 285         if (inCallExpr) {
 286             if (e.getFields().length() == 1) {
 287                 selectedField = e.getFields().charAt(0);
 288             } else {
 289                 int index = inVectorOp ? vectorIndex : 0;
 290                 selectedField = e.getFields().charAt(index);
 291             }
 292             inFieldSelect = true;
 293             scan(e.getExpr());
 294             inFieldSelect = false;
 295         } else {
 296             selectedFields = getFieldSet(e.getFields());
 297             super.visitFieldSelectExpr(e);
 298             selectedFields = null;
 299         }
 300     }
 301 
 302     private static Set<Integer> getFieldSet(String fields) {
 303         Set<Integer> fieldSet = new HashSet<Integer>();
 304         for (int i = 0; i < fields.length(); i++) {
 305             fieldSet.add(getFieldIndex(fields.charAt(i)));
 306         }
 307         return fieldSet;
 308     }
 309 
 310     @Override
 311     public void visitLiteralExpr(LiteralExpr e) {
 312         if (inCallExpr) {
 313             output(e.getValue().toString());
 314             if (e.getValue() instanceof Float) {
 315                 output("f");
 316             }
 317         } else {
 318             super.visitLiteralExpr(e);
 319         }
 320     }
 321 
 322     @Override
 323     public void visitParenExpr(ParenExpr e) {
 324         if (inCallExpr) {
 325             output("(");
 326             scan(e.getExpr());
 327             output(")");
 328         } else {
 329             super.visitParenExpr(e);
 330         }
 331     }
 332 
 333     @Override
 334     public void visitUnaryExpr(UnaryExpr e) {
 335         if (inCallExpr) {
 336             output(e.getOp().toString());
 337             scan(e.getExpr());
 338         } else {
 339             super.visitUnaryExpr(e);
 340         }
 341     }
 342 
 343     @Override
 344     public void visitVariableExpr(VariableExpr e) {
 345         if (inCallExpr) {
 346             Variable var = e.getVariable();
 347             output(var.getName());
 348             if (var.isParam()) {
 349                 output("_tmp");
 350             }
 351             if (var.getType().isVector()) {
 352                 if (inFieldSelect) {
 353                     output(getSuffix(getFieldIndex(selectedField)));
 354                 } else if (inVectorOp) {
 355                     output(getSuffix(vectorIndex));
 356                 } else {
 357                     throw new InternalError("TBD");
 358                 }
 359             }
 360         } else {
 361             super.visitVariableExpr(e);
 362         }
 363     }
 364 
 365     @Override
 366     public void visitVectorCtorExpr(VectorCtorExpr e) {
 367         // TODO: this will likely work for simple variables and literals,
 368         // but we need something more for embedded function calls, etc...
 369         scan(e.getParams().get(vectorIndex));
 370     }
 371 }