@Test public void testRemoveIfOrLoopButKeepGuard() throws Exception { final String shaderIf = "#version 310 es\n" + "void main() {\n" + " if(1 > 0) {\n" + " }\n" + "}\n"; final String shaderWhile = "#version 310 es\n" + "void main() {\n" + " while(1 > 0) {\n" + " }\n" + "}\n"; final String shaderDoWhile = "#version 310 es\n" + "void main() {\n" + " do {\n" + " } while(1 > 0);\n" + "}\n"; final String shaderFor = "#version 310 es\n" + "void main() {\n" + " for (; 1 > 0; ) {\n" + " }\n" + "}\n"; final String expected = "#version 310 es\n" + "void main() {\n" + " 1 > 0;\n" + "}\n"; final IFileJudge customFileJudge = new CheckAstFeaturesFileJudge(Collections.singletonList( () -> new CheckAstFeatureVisitor() { @Override public void visitBinaryExpr(BinaryExpr binaryExpr) { super.visitBinaryExpr(binaryExpr); if (binaryExpr.getOp() == BinOp.GT && binaryExpr.getLhs() instanceof IntConstantExpr && ((IntConstantExpr) binaryExpr.getLhs()).getNumericValue() == 1 && binaryExpr.getRhs() instanceof IntConstantExpr && ((IntConstantExpr) binaryExpr.getRhs()).getNumericValue() == 0) { trigger(); } } }), ShaderKind.FRAGMENT, fileOps); for (String shader : Arrays.asList(shaderIf, shaderWhile, shaderDoWhile, shaderFor)) { final ShaderJob shaderJob = new GlslShaderJob(Optional.empty(), new PipelineInfo(), ParseHelper.parse(shader)); final File workDir = testFolder.getRoot(); final File tempShaderJobFile = new File(workDir, "temp.json"); fileOps.writeShaderJobFile(shaderJob, tempShaderJobFile); final String resultsPrefix = new ReductionDriver(new ReducerContext(true, ShadingLanguageVersion.ESSL_310, new RandomWrapper(0), new IdGenerator()), false, fileOps, customFileJudge, workDir) .doReduction(shaderJob, "temp", 0, 100); CompareAsts.assertEqualAsts(expected, ParseHelper.parse(new File(testFolder.getRoot(), resultsPrefix + ".frag"))); } }
public String doReduction( ShaderJob initialState, String shaderJobShortName, int fileCountOffset, int stepLimit) throws IOException { final boolean requiresUniformBindings = initialState.getPipelineInfo().getNumUniforms() == 0 || initialState.hasUniformBindings(); final Optional<String> pushConstant = initialState.getPushConstant(); if (initialState.hasUniformBindings()) { initialState.removeUniformBindings(); } try { if (fileCountOffset > 0) { LOGGER.info("Continuing reduction for {}", shaderJobShortName); } else { LOGGER.info("Starting reduction for {}", shaderJobShortName); for (int i = 1; ; i++) { if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, false, false, false, shaderJobShortName)) { break; } LOGGER.info("Result from initial state is not interesting (attempt " + i + ")"); if (i >= NUM_INITIAL_TRIES) { LOGGER.info("Tried " + NUM_INITIAL_TRIES + " times; stopping."); fileOps.createFile(new File(workDir, "NOT_INTERESTING")); return null; } } LOGGER.info("Result from initial state is interesting - proceeding with reduction."); } boolean addGlobalLoopLimiters = false; boolean makeArrayAccessesInBounds = false; boolean addInitializers = false; if (context.reduceEverywhere()) { LOGGER.info("We are not preserving semantics, so see whether adding guards against " + "undefined behaviour preserves interestingness."); if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, true, false, false, shaderJobShortName)) { LOGGER.info("The shader is still interesting when global loop limiters are added; these" + " will be used during reduction, so the final reduced shader should be free from " + "long-running or infinite loops."); addGlobalLoopLimiters = true; } else { LOGGER.info("The shader is not interesting when global loop limiters are added; these" + " will not be used during reduction, so the final reduced shader could contain " + "long-running or infinite loops."); } if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, true, false, shaderJobShortName)) { LOGGER.info("The shader is still interesting when array/vector/matrix accesses " + "are made in-bounds. Bounds clamping will be applied during reduction, so " + "the final reduced shader should be free from out-of-bounds accesses."); makeArrayAccessesInBounds = true; } else { LOGGER.info("The shader is not interesting when array/vector/matrix accesses " + "are made in-bounds. Bounds clamping will be not be applied during reduction, " + "so the final reduced shader might exhibit out-of-bounds accesses."); } if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, true, shaderJobShortName)) { LOGGER.info("The shader is still interesting when initializers are added to all regular " + "variables. Adding of initializers will be applied during reduction, so the final " + "reduced shader should be free from reads from uninitialized regular variables."); addInitializers = true; } else { LOGGER.info("The shader is not interesting when initializers are added to all regular " + "variables. Adding of initializers will not be applied during reduction, so the " + "final reduced shader should be free from reads from uninitialized regular " + "variables."); } } ShaderJob currentState = initialState; int stepCount = 0; boolean stoppedEarly = false; while (true) { LOGGER.info("Trying reduction attempt " + stepCount + " (" + numSuccessfulReductions + " successful so far)."); final Optional<ShaderJob> maybeNewState = passManager.applyReduction(currentState); if (!maybeNewState.isPresent()) { LOGGER.info("No more to reduce; stopping."); break; } final ShaderJob newState = maybeNewState.get(); stepCount++; final int currentReductionAttempt = stepCount + fileCountOffset; String currentShaderJobShortName = getReductionStepShaderJobShortName( shaderJobShortName, currentReductionAttempt); final boolean interesting = isInterestingWithCache(newState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers, currentShaderJobShortName); passManager.notifyInteresting(interesting); final String currentStepShaderJobShortNameWithOutcome = getReductionStepShaderJobShortName( shaderJobShortName, currentReductionAttempt, Optional.of(interesting ? "success" : "fail")); fileOps.moveShaderJobFileTo( new File(workDir, currentShaderJobShortName + ".json"), new File(workDir, currentStepShaderJobShortNameWithOutcome + ".json"), true ); if (interesting) { LOGGER.info("Successful reduction."); numSuccessfulReductions++; currentState = newState; } else { LOGGER.info("Failed reduction."); } if (stepLimit > -1 && stepCount >= stepLimit) { LOGGER.info("Stopping reduction due to hitting step limit {}.", stepLimit); stoppedEarly = true; break; } } ShaderJob finalState = finaliseReduction(currentState); String finalOutputFilePrefix = shaderJobShortName + "_reduced_final"; if (!isInterestingNoCache(finalState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers, finalOutputFilePrefix)) { LOGGER.info( "Failed to simplify final reduction state! Reverting to the non-simplified state."); writeState(currentState, new File(workDir, finalOutputFilePrefix + ".json"), requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers); } if (stoppedEarly) { fileOps.createFile(new File(workDir, Constants.REDUCTION_INCOMPLETE)); } LOGGER.info("Total fail hash cache hits: " + failHashCacheHits); return finalOutputFilePrefix; } catch (FileNotFoundException | FileJudgeException exception) { throw new RuntimeException(exception); } }
ReductionDriver { public String doReduction( ShaderJob initialState, String shaderJobShortName, int fileCountOffset, int stepLimit) throws IOException { final boolean requiresUniformBindings = initialState.getPipelineInfo().getNumUniforms() == 0 || initialState.hasUniformBindings(); final Optional<String> pushConstant = initialState.getPushConstant(); if (initialState.hasUniformBindings()) { initialState.removeUniformBindings(); } try { if (fileCountOffset > 0) { LOGGER.info("Continuing reduction for {}", shaderJobShortName); } else { LOGGER.info("Starting reduction for {}", shaderJobShortName); for (int i = 1; ; i++) { if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, false, false, false, shaderJobShortName)) { break; } LOGGER.info("Result from initial state is not interesting (attempt " + i + ")"); if (i >= NUM_INITIAL_TRIES) { LOGGER.info("Tried " + NUM_INITIAL_TRIES + " times; stopping."); fileOps.createFile(new File(workDir, "NOT_INTERESTING")); return null; } } LOGGER.info("Result from initial state is interesting - proceeding with reduction."); } boolean addGlobalLoopLimiters = false; boolean makeArrayAccessesInBounds = false; boolean addInitializers = false; if (context.reduceEverywhere()) { LOGGER.info("We are not preserving semantics, so see whether adding guards against " + "undefined behaviour preserves interestingness."); if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, true, false, false, shaderJobShortName)) { LOGGER.info("The shader is still interesting when global loop limiters are added; these" + " will be used during reduction, so the final reduced shader should be free from " + "long-running or infinite loops."); addGlobalLoopLimiters = true; } else { LOGGER.info("The shader is not interesting when global loop limiters are added; these" + " will not be used during reduction, so the final reduced shader could contain " + "long-running or infinite loops."); } if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, true, false, shaderJobShortName)) { LOGGER.info("The shader is still interesting when array/vector/matrix accesses " + "are made in-bounds. Bounds clamping will be applied during reduction, so " + "the final reduced shader should be free from out-of-bounds accesses."); makeArrayAccessesInBounds = true; } else { LOGGER.info("The shader is not interesting when array/vector/matrix accesses " + "are made in-bounds. Bounds clamping will be not be applied during reduction, " + "so the final reduced shader might exhibit out-of-bounds accesses."); } if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, true, shaderJobShortName)) { LOGGER.info("The shader is still interesting when initializers are added to all regular " + "variables. Adding of initializers will be applied during reduction, so the final " + "reduced shader should be free from reads from uninitialized regular variables."); addInitializers = true; } else { LOGGER.info("The shader is not interesting when initializers are added to all regular " + "variables. Adding of initializers will not be applied during reduction, so the " + "final reduced shader should be free from reads from uninitialized regular " + "variables."); } } ShaderJob currentState = initialState; int stepCount = 0; boolean stoppedEarly = false; while (true) { LOGGER.info("Trying reduction attempt " + stepCount + " (" + numSuccessfulReductions + " successful so far)."); final Optional<ShaderJob> maybeNewState = passManager.applyReduction(currentState); if (!maybeNewState.isPresent()) { LOGGER.info("No more to reduce; stopping."); break; } final ShaderJob newState = maybeNewState.get(); stepCount++; final int currentReductionAttempt = stepCount + fileCountOffset; String currentShaderJobShortName = getReductionStepShaderJobShortName( shaderJobShortName, currentReductionAttempt); final boolean interesting = isInterestingWithCache(newState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers, currentShaderJobShortName); passManager.notifyInteresting(interesting); final String currentStepShaderJobShortNameWithOutcome = getReductionStepShaderJobShortName( shaderJobShortName, currentReductionAttempt, Optional.of(interesting ? "success" : "fail")); fileOps.moveShaderJobFileTo( new File(workDir, currentShaderJobShortName + ".json"), new File(workDir, currentStepShaderJobShortNameWithOutcome + ".json"), true ); if (interesting) { LOGGER.info("Successful reduction."); numSuccessfulReductions++; currentState = newState; } else { LOGGER.info("Failed reduction."); } if (stepLimit > -1 && stepCount >= stepLimit) { LOGGER.info("Stopping reduction due to hitting step limit {}.", stepLimit); stoppedEarly = true; break; } } ShaderJob finalState = finaliseReduction(currentState); String finalOutputFilePrefix = shaderJobShortName + "_reduced_final"; if (!isInterestingNoCache(finalState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers, finalOutputFilePrefix)) { LOGGER.info( "Failed to simplify final reduction state! Reverting to the non-simplified state."); writeState(currentState, new File(workDir, finalOutputFilePrefix + ".json"), requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers); } if (stoppedEarly) { fileOps.createFile(new File(workDir, Constants.REDUCTION_INCOMPLETE)); } LOGGER.info("Total fail hash cache hits: " + failHashCacheHits); return finalOutputFilePrefix; } catch (FileNotFoundException | FileJudgeException exception) { throw new RuntimeException(exception); } } }
ReductionDriver { public String doReduction( ShaderJob initialState, String shaderJobShortName, int fileCountOffset, int stepLimit) throws IOException { final boolean requiresUniformBindings = initialState.getPipelineInfo().getNumUniforms() == 0 || initialState.hasUniformBindings(); final Optional<String> pushConstant = initialState.getPushConstant(); if (initialState.hasUniformBindings()) { initialState.removeUniformBindings(); } try { if (fileCountOffset > 0) { LOGGER.info("Continuing reduction for {}", shaderJobShortName); } else { LOGGER.info("Starting reduction for {}", shaderJobShortName); for (int i = 1; ; i++) { if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, false, false, false, shaderJobShortName)) { break; } LOGGER.info("Result from initial state is not interesting (attempt " + i + ")"); if (i >= NUM_INITIAL_TRIES) { LOGGER.info("Tried " + NUM_INITIAL_TRIES + " times; stopping."); fileOps.createFile(new File(workDir, "NOT_INTERESTING")); return null; } } LOGGER.info("Result from initial state is interesting - proceeding with reduction."); } boolean addGlobalLoopLimiters = false; boolean makeArrayAccessesInBounds = false; boolean addInitializers = false; if (context.reduceEverywhere()) { LOGGER.info("We are not preserving semantics, so see whether adding guards against " + "undefined behaviour preserves interestingness."); if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, true, false, false, shaderJobShortName)) { LOGGER.info("The shader is still interesting when global loop limiters are added; these" + " will be used during reduction, so the final reduced shader should be free from " + "long-running or infinite loops."); addGlobalLoopLimiters = true; } else { LOGGER.info("The shader is not interesting when global loop limiters are added; these" + " will not be used during reduction, so the final reduced shader could contain " + "long-running or infinite loops."); } if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, true, false, shaderJobShortName)) { LOGGER.info("The shader is still interesting when array/vector/matrix accesses " + "are made in-bounds. Bounds clamping will be applied during reduction, so " + "the final reduced shader should be free from out-of-bounds accesses."); makeArrayAccessesInBounds = true; } else { LOGGER.info("The shader is not interesting when array/vector/matrix accesses " + "are made in-bounds. Bounds clamping will be not be applied during reduction, " + "so the final reduced shader might exhibit out-of-bounds accesses."); } if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, true, shaderJobShortName)) { LOGGER.info("The shader is still interesting when initializers are added to all regular " + "variables. Adding of initializers will be applied during reduction, so the final " + "reduced shader should be free from reads from uninitialized regular variables."); addInitializers = true; } else { LOGGER.info("The shader is not interesting when initializers are added to all regular " + "variables. Adding of initializers will not be applied during reduction, so the " + "final reduced shader should be free from reads from uninitialized regular " + "variables."); } } ShaderJob currentState = initialState; int stepCount = 0; boolean stoppedEarly = false; while (true) { LOGGER.info("Trying reduction attempt " + stepCount + " (" + numSuccessfulReductions + " successful so far)."); final Optional<ShaderJob> maybeNewState = passManager.applyReduction(currentState); if (!maybeNewState.isPresent()) { LOGGER.info("No more to reduce; stopping."); break; } final ShaderJob newState = maybeNewState.get(); stepCount++; final int currentReductionAttempt = stepCount + fileCountOffset; String currentShaderJobShortName = getReductionStepShaderJobShortName( shaderJobShortName, currentReductionAttempt); final boolean interesting = isInterestingWithCache(newState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers, currentShaderJobShortName); passManager.notifyInteresting(interesting); final String currentStepShaderJobShortNameWithOutcome = getReductionStepShaderJobShortName( shaderJobShortName, currentReductionAttempt, Optional.of(interesting ? "success" : "fail")); fileOps.moveShaderJobFileTo( new File(workDir, currentShaderJobShortName + ".json"), new File(workDir, currentStepShaderJobShortNameWithOutcome + ".json"), true ); if (interesting) { LOGGER.info("Successful reduction."); numSuccessfulReductions++; currentState = newState; } else { LOGGER.info("Failed reduction."); } if (stepLimit > -1 && stepCount >= stepLimit) { LOGGER.info("Stopping reduction due to hitting step limit {}.", stepLimit); stoppedEarly = true; break; } } ShaderJob finalState = finaliseReduction(currentState); String finalOutputFilePrefix = shaderJobShortName + "_reduced_final"; if (!isInterestingNoCache(finalState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers, finalOutputFilePrefix)) { LOGGER.info( "Failed to simplify final reduction state! Reverting to the non-simplified state."); writeState(currentState, new File(workDir, finalOutputFilePrefix + ".json"), requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers); } if (stoppedEarly) { fileOps.createFile(new File(workDir, Constants.REDUCTION_INCOMPLETE)); } LOGGER.info("Total fail hash cache hits: " + failHashCacheHits); return finalOutputFilePrefix; } catch (FileNotFoundException | FileJudgeException exception) { throw new RuntimeException(exception); } } ReductionDriver(ReducerContext context,
                         boolean verbose,
                         ShaderJobFileOperations fileOps,
                         IFileJudge judge,
                         File workDir);  ReductionDriver(ReducerContext context,
                         boolean verbose,
                         ShaderJobFileOperations fileOps,
                         IFileJudge judge,
                         File workDir,
                         boolean literalsToUniforms); }
ReductionDriver { public String doReduction( ShaderJob initialState, String shaderJobShortName, int fileCountOffset, int stepLimit) throws IOException { final boolean requiresUniformBindings = initialState.getPipelineInfo().getNumUniforms() == 0 || initialState.hasUniformBindings(); final Optional<String> pushConstant = initialState.getPushConstant(); if (initialState.hasUniformBindings()) { initialState.removeUniformBindings(); } try { if (fileCountOffset > 0) { LOGGER.info("Continuing reduction for {}", shaderJobShortName); } else { LOGGER.info("Starting reduction for {}", shaderJobShortName); for (int i = 1; ; i++) { if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, false, false, false, shaderJobShortName)) { break; } LOGGER.info("Result from initial state is not interesting (attempt " + i + ")"); if (i >= NUM_INITIAL_TRIES) { LOGGER.info("Tried " + NUM_INITIAL_TRIES + " times; stopping."); fileOps.createFile(new File(workDir, "NOT_INTERESTING")); return null; } } LOGGER.info("Result from initial state is interesting - proceeding with reduction."); } boolean addGlobalLoopLimiters = false; boolean makeArrayAccessesInBounds = false; boolean addInitializers = false; if (context.reduceEverywhere()) { LOGGER.info("We are not preserving semantics, so see whether adding guards against " + "undefined behaviour preserves interestingness."); if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, true, false, false, shaderJobShortName)) { LOGGER.info("The shader is still interesting when global loop limiters are added; these" + " will be used during reduction, so the final reduced shader should be free from " + "long-running or infinite loops."); addGlobalLoopLimiters = true; } else { LOGGER.info("The shader is not interesting when global loop limiters are added; these" + " will not be used during reduction, so the final reduced shader could contain " + "long-running or infinite loops."); } if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, true, false, shaderJobShortName)) { LOGGER.info("The shader is still interesting when array/vector/matrix accesses " + "are made in-bounds. Bounds clamping will be applied during reduction, so " + "the final reduced shader should be free from out-of-bounds accesses."); makeArrayAccessesInBounds = true; } else { LOGGER.info("The shader is not interesting when array/vector/matrix accesses " + "are made in-bounds. Bounds clamping will be not be applied during reduction, " + "so the final reduced shader might exhibit out-of-bounds accesses."); } if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, true, shaderJobShortName)) { LOGGER.info("The shader is still interesting when initializers are added to all regular " + "variables. Adding of initializers will be applied during reduction, so the final " + "reduced shader should be free from reads from uninitialized regular variables."); addInitializers = true; } else { LOGGER.info("The shader is not interesting when initializers are added to all regular " + "variables. Adding of initializers will not be applied during reduction, so the " + "final reduced shader should be free from reads from uninitialized regular " + "variables."); } } ShaderJob currentState = initialState; int stepCount = 0; boolean stoppedEarly = false; while (true) { LOGGER.info("Trying reduction attempt " + stepCount + " (" + numSuccessfulReductions + " successful so far)."); final Optional<ShaderJob> maybeNewState = passManager.applyReduction(currentState); if (!maybeNewState.isPresent()) { LOGGER.info("No more to reduce; stopping."); break; } final ShaderJob newState = maybeNewState.get(); stepCount++; final int currentReductionAttempt = stepCount + fileCountOffset; String currentShaderJobShortName = getReductionStepShaderJobShortName( shaderJobShortName, currentReductionAttempt); final boolean interesting = isInterestingWithCache(newState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers, currentShaderJobShortName); passManager.notifyInteresting(interesting); final String currentStepShaderJobShortNameWithOutcome = getReductionStepShaderJobShortName( shaderJobShortName, currentReductionAttempt, Optional.of(interesting ? "success" : "fail")); fileOps.moveShaderJobFileTo( new File(workDir, currentShaderJobShortName + ".json"), new File(workDir, currentStepShaderJobShortNameWithOutcome + ".json"), true ); if (interesting) { LOGGER.info("Successful reduction."); numSuccessfulReductions++; currentState = newState; } else { LOGGER.info("Failed reduction."); } if (stepLimit > -1 && stepCount >= stepLimit) { LOGGER.info("Stopping reduction due to hitting step limit {}.", stepLimit); stoppedEarly = true; break; } } ShaderJob finalState = finaliseReduction(currentState); String finalOutputFilePrefix = shaderJobShortName + "_reduced_final"; if (!isInterestingNoCache(finalState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers, finalOutputFilePrefix)) { LOGGER.info( "Failed to simplify final reduction state! Reverting to the non-simplified state."); writeState(currentState, new File(workDir, finalOutputFilePrefix + ".json"), requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers); } if (stoppedEarly) { fileOps.createFile(new File(workDir, Constants.REDUCTION_INCOMPLETE)); } LOGGER.info("Total fail hash cache hits: " + failHashCacheHits); return finalOutputFilePrefix; } catch (FileNotFoundException | FileJudgeException exception) { throw new RuntimeException(exception); } } ReductionDriver(ReducerContext context,
                         boolean verbose,
                         ShaderJobFileOperations fileOps,
                         IFileJudge judge,
                         File workDir);  ReductionDriver(ReducerContext context,
                         boolean verbose,
                         ShaderJobFileOperations fileOps,
                         IFileJudge judge,
                         File workDir,
                         boolean literalsToUniforms); String doReduction(
        ShaderJob initialState,
        String shaderJobShortName,
        int fileCountOffset, // Used when continuing a reduction - added on to the number associated
        // with each reduction step during the current reduction.
        int stepLimit); static String getReductionStepShaderJobShortName(String variantPrefix,
                                                          int currentReductionAttempt,
                                                          Optional<String> successIndicator); }
ReductionDriver { public String doReduction( ShaderJob initialState, String shaderJobShortName, int fileCountOffset, int stepLimit) throws IOException { final boolean requiresUniformBindings = initialState.getPipelineInfo().getNumUniforms() == 0 || initialState.hasUniformBindings(); final Optional<String> pushConstant = initialState.getPushConstant(); if (initialState.hasUniformBindings()) { initialState.removeUniformBindings(); } try { if (fileCountOffset > 0) { LOGGER.info("Continuing reduction for {}", shaderJobShortName); } else { LOGGER.info("Starting reduction for {}", shaderJobShortName); for (int i = 1; ; i++) { if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, false, false, false, shaderJobShortName)) { break; } LOGGER.info("Result from initial state is not interesting (attempt " + i + ")"); if (i >= NUM_INITIAL_TRIES) { LOGGER.info("Tried " + NUM_INITIAL_TRIES + " times; stopping."); fileOps.createFile(new File(workDir, "NOT_INTERESTING")); return null; } } LOGGER.info("Result from initial state is interesting - proceeding with reduction."); } boolean addGlobalLoopLimiters = false; boolean makeArrayAccessesInBounds = false; boolean addInitializers = false; if (context.reduceEverywhere()) { LOGGER.info("We are not preserving semantics, so see whether adding guards against " + "undefined behaviour preserves interestingness."); if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, true, false, false, shaderJobShortName)) { LOGGER.info("The shader is still interesting when global loop limiters are added; these" + " will be used during reduction, so the final reduced shader should be free from " + "long-running or infinite loops."); addGlobalLoopLimiters = true; } else { LOGGER.info("The shader is not interesting when global loop limiters are added; these" + " will not be used during reduction, so the final reduced shader could contain " + "long-running or infinite loops."); } if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, true, false, shaderJobShortName)) { LOGGER.info("The shader is still interesting when array/vector/matrix accesses " + "are made in-bounds. Bounds clamping will be applied during reduction, so " + "the final reduced shader should be free from out-of-bounds accesses."); makeArrayAccessesInBounds = true; } else { LOGGER.info("The shader is not interesting when array/vector/matrix accesses " + "are made in-bounds. Bounds clamping will be not be applied during reduction, " + "so the final reduced shader might exhibit out-of-bounds accesses."); } if (isInterestingNoCache(initialState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, true, shaderJobShortName)) { LOGGER.info("The shader is still interesting when initializers are added to all regular " + "variables. Adding of initializers will be applied during reduction, so the final " + "reduced shader should be free from reads from uninitialized regular variables."); addInitializers = true; } else { LOGGER.info("The shader is not interesting when initializers are added to all regular " + "variables. Adding of initializers will not be applied during reduction, so the " + "final reduced shader should be free from reads from uninitialized regular " + "variables."); } } ShaderJob currentState = initialState; int stepCount = 0; boolean stoppedEarly = false; while (true) { LOGGER.info("Trying reduction attempt " + stepCount + " (" + numSuccessfulReductions + " successful so far)."); final Optional<ShaderJob> maybeNewState = passManager.applyReduction(currentState); if (!maybeNewState.isPresent()) { LOGGER.info("No more to reduce; stopping."); break; } final ShaderJob newState = maybeNewState.get(); stepCount++; final int currentReductionAttempt = stepCount + fileCountOffset; String currentShaderJobShortName = getReductionStepShaderJobShortName( shaderJobShortName, currentReductionAttempt); final boolean interesting = isInterestingWithCache(newState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers, currentShaderJobShortName); passManager.notifyInteresting(interesting); final String currentStepShaderJobShortNameWithOutcome = getReductionStepShaderJobShortName( shaderJobShortName, currentReductionAttempt, Optional.of(interesting ? "success" : "fail")); fileOps.moveShaderJobFileTo( new File(workDir, currentShaderJobShortName + ".json"), new File(workDir, currentStepShaderJobShortNameWithOutcome + ".json"), true ); if (interesting) { LOGGER.info("Successful reduction."); numSuccessfulReductions++; currentState = newState; } else { LOGGER.info("Failed reduction."); } if (stepLimit > -1 && stepCount >= stepLimit) { LOGGER.info("Stopping reduction due to hitting step limit {}.", stepLimit); stoppedEarly = true; break; } } ShaderJob finalState = finaliseReduction(currentState); String finalOutputFilePrefix = shaderJobShortName + "_reduced_final"; if (!isInterestingNoCache(finalState, requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers, finalOutputFilePrefix)) { LOGGER.info( "Failed to simplify final reduction state! Reverting to the non-simplified state."); writeState(currentState, new File(workDir, finalOutputFilePrefix + ".json"), requiresUniformBindings, pushConstant, addGlobalLoopLimiters, makeArrayAccessesInBounds, addInitializers); } if (stoppedEarly) { fileOps.createFile(new File(workDir, Constants.REDUCTION_INCOMPLETE)); } LOGGER.info("Total fail hash cache hits: " + failHashCacheHits); return finalOutputFilePrefix; } catch (FileNotFoundException | FileJudgeException exception) { throw new RuntimeException(exception); } } ReductionDriver(ReducerContext context,
                         boolean verbose,
                         ShaderJobFileOperations fileOps,
                         IFileJudge judge,
                         File workDir);  ReductionDriver(ReducerContext context,
                         boolean verbose,
                         ShaderJobFileOperations fileOps,
                         IFileJudge judge,
                         File workDir,
                         boolean literalsToUniforms); String doReduction(
        ShaderJob initialState,
        String shaderJobShortName,
        int fileCountOffset, // Used when continuing a reduction - added on to the number associated
        // with each reduction step during the current reduction.
        int stepLimit); static String getReductionStepShaderJobShortName(String variantPrefix,
                                                          int currentReductionAttempt,
                                                          Optional<String> successIndicator); static final boolean DEBUG_REDUCER; }