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 }