GloballyTruncateLoops { public static void truncate(ShaderJob shaderJob, int loopLimit, String loopCountName, String loopBoundName) { for (TranslationUnit tu : shaderJob.getShaders()) { final boolean globalDeclarationsAreNecessary = new StandardVisitor() { private boolean appliedAtLeastOneTruncation = false; private Expr buildCondition(Expr originalCondition) { return new BinaryExpr( new ParenExpr(originalCondition), new ParenExpr( new BinaryExpr(new VariableIdentifierExpr(loopCountName), new VariableIdentifierExpr(loopBoundName), BinOp.LT)), BinOp.LAND); } private boolean isFalseLiteral(Expr conditionExpr) { return !(conditionExpr instanceof BoolConstantExpr && !((BoolConstantExpr)conditionExpr).getIsTrue()); } private void handleLoopStmt(LoopStmt loopStmt, boolean newScope) { if (isFalseLiteral(loopStmt.getCondition())) { appliedAtLeastOneTruncation = true; loopStmt.setCondition(buildCondition(loopStmt.getCondition())); if (!(loopStmt.getBody() instanceof BlockStmt)) { loopStmt.setBody(new BlockStmt(Collections.singletonList(loopStmt.getBody()), newScope)); } ((BlockStmt) loopStmt.getBody()).insertStmt(0, new ExprStmt(new UnaryExpr(new VariableIdentifierExpr(loopCountName), UnOp.POST_INC))); } } @Override public void visitForStmt(ForStmt forStmt) { handleLoopStmt(forStmt, true); super.visitForStmt(forStmt); } @Override public void visitWhileStmt(WhileStmt whileStmt) { handleLoopStmt(whileStmt, false); super.visitWhileStmt(whileStmt); } @Override public void visitDoStmt(DoStmt doStmt) { handleLoopStmt(doStmt, true); super.visitDoStmt(doStmt); } private boolean truncate(TranslationUnit tu) { visit(tu); return appliedAtLeastOneTruncation; } }.truncate(tu); if (globalDeclarationsAreNecessary) { Declaration firstNonPrecisionDeclaration = null; for (Declaration decl : tu.getTopLevelDeclarations()) { if (decl instanceof PrecisionDeclaration) { continue; } firstNonPrecisionDeclaration = decl; break; } assert firstNonPrecisionDeclaration != null; tu.addDeclarationBefore(new VariablesDeclaration(new QualifiedType(BasicType.INT, Arrays.asList(TypeQualifier.CONST)), new VariableDeclInfo(loopBoundName, null, new Initializer(new IntConstantExpr(new Integer(loopLimit).toString())))), firstNonPrecisionDeclaration); tu.addDeclarationBefore(new VariablesDeclaration(BasicType.INT, new VariableDeclInfo(loopCountName, null, new Initializer(new IntConstantExpr("0")))), firstNonPrecisionDeclaration); } } }  static void truncate(ShaderJob shaderJob, int loopLimit, String loopCountName,
                              String loopBoundName);  }
@Test public void basicTest() throws Exception { final String shader = "#version 310 es\n" + "void foo(int x) {\n" + " for (int i = 0; i < 100; ) ;\n" + " for (int i = 0; i < 100; ) {\n" + " int j = 0;\n" + " while (j < x) {\n" + " j++;\n" + " }\n" + " while (false) {\n" + " j++;\n" + " }\n" + " }\n" + "}\n" + "void main() {\n" + " do {\n" + " foo(1);\n" + " } while(false);\n" + " do {\n" + " foo(1);\n" + " } while(true);\n" + " for (int i = 0; false; i++) {\n" + " }\n" + " for (int i = 0; false; i++) ;\n" + "}\n"; final ShaderJob shaderJob = new GlslShaderJob(Optional.empty(), new PipelineInfo(), ParseHelper.parse(shader, ShaderKind.VERTEX), ParseHelper.parse(shader, ShaderKind.FRAGMENT)); GloballyTruncateLoops.truncate(shaderJob, 100, "LOOP_COUNT", "LOOP_BOUND"); final String expectedShader = "#version 310 es\n" + "const int LOOP_BOUND = 100;\n" + "int LOOP_COUNT = 0;\n" + "void foo(int x) {\n" + " for (int i = 0; (i < 100) && (LOOP_COUNT < LOOP_BOUND); ) {\n" + " LOOP_COUNT++;\n" + " ;\n" + " }\n" + " for (int i = 0; (i < 100) && (LOOP_COUNT < LOOP_BOUND); ) {\n" + " LOOP_COUNT++;\n" + " int j = 0;\n" + " while ((j < x) && (LOOP_COUNT < LOOP_BOUND)) {\n" + " LOOP_COUNT++;\n" + " j++;\n" + " }\n" + " while (false) {\n" + " j++;\n" + " }\n" + " }\n" + "}\n" + "void main() {\n" + " do {\n" + " foo(1);\n" + " } while(false);\n" + " do {\n" + " LOOP_COUNT++;\n" + " foo(1);\n" + " } while((true) && (LOOP_COUNT < LOOP_BOUND));\n" + " for (int i = 0; false; i++) {\n" + " }\n" + " for (int i = 0; false; i++) ;\n" + "}\n"; for (TranslationUnit tu : shaderJob.getShaders()) { CompareAsts.assertEqualAsts(expectedShader, tu); } }
@Test public void doNotAddDeclarationsIfNothingToTruncate() throws Exception { final String shader = "#version 310 es\n" + "void main() {\n" + " do {\n" + " } while(false);\n" + "}\n"; final ShaderJob shaderJob = new GlslShaderJob(Optional.empty(), new PipelineInfo(), ParseHelper.parse(shader, ShaderKind.VERTEX), ParseHelper.parse(shader, ShaderKind.FRAGMENT)); GloballyTruncateLoops.truncate(shaderJob, 100, "LOOP_COUNT", "LOOP_BOUND"); for (TranslationUnit tu : shaderJob.getShaders()) { CompareAsts.assertEqualAsts(shader, tu); } }