uniformValues = new ArrayList<>();
+ if (value instanceof CompositeValue) {
+ CompositeValue compositeValue = (CompositeValue) value;
+ for (Value element : compositeValue.getValueList().get()) {
+ // It's also possible that an element of the given composite value is unknown, thus we have
+ // to initialize the element if it is empty.
+ final NumericValue elementValue = element.valueIsUnknown()
+ ? (NumericValue) fuzzValue(element.getType(), true)
+ : (NumericValue) element;
+ uniformValues.add(elementValue.getValue().get());
+ }
+ } else {
+ assert value instanceof NumericValue;
+ uniformValues.add(((NumericValue) value).getValue().get());
+ }
+
+ pipelineInfo.addUniform(uniformName, (BasicType) value.getType(), Optional.empty(),
+ uniformValues);
+ // Declare a new variable declaration for the uniform we generated.
+ final VariableDeclInfo variableDeclInfo = new VariableDeclInfo(uniformName, null, null);
+ final VariablesDeclaration variablesDecl = new VariablesDeclaration(
+ new QualifiedType(value.getType(), Arrays.asList(TypeQualifier.UNIFORM)), variableDeclInfo
+ );
+ translationUnit.addDeclaration(variablesDecl);
+
+ // As uniform variables are available at the global scope, we create a new fact given a
+ // generated uniform and thus keep it at the global scope manager.
+ final VariableDeclFact variableDeclFact = new VariableDeclFact(variablesDecl,
+ variableDeclInfo, value);
+ globalFactManager.addVariableFact(value, variableDeclFact);
+
+ return new VariableIdentifierExpr(uniformName);
+ }
+
+ /**
+ * Assigns value to the newly-generated variable using a for loop. To do so, we first pick a
+ * random number (divisor) and add this number to the variable for each iteration. Then, we
+ * basically find a number of iterations by dividing the original value by the chosen divisor
+ * . We would also have to eventually add the remainder to the variable if needed.
+ *
+ * For example, given a value x we can derive a for loop statement and a remainder from the
+ * equation x = (divisor * iterations) + remainder.
+ *
+ *
The code fragment below is an example result obtained by this method which returns an int 7.
+ * int _GLF_PRIMITIVE_int_7 = 0;
+ * // 3 is the number of iterations obtained by dividing the given value 7 by the random
+ * divisor 2.
+ * for(int i = 0; i < 3; i ++) {
+ * _GLF_PRIMITIVE_int_7 += 2;
+ * }
+ * _GLF_PRIMITIVE_int_7 += 1; // 1 is a remainder of 7 divided by 2.
+ *
+ * @param value the value that will be computed by the expression generated by
+ * this method.
+ * @param factManager manager class holding the value and its associated expression that
+ * guarantees to compute the given value.
+ * @param currentFunction a function into which the new expression will be injected.
+ * @param stmtToInsertBefore statement in the body of the given function where the generated
+ * expression will be inserted before.
+ * @return a variable identifier to the new variable generated by this method.
+ */
+ private Expr generateForLoopValue(Value value,
+ FactManager factManager,
+ FunctionDefinition currentFunction,
+ Stmt stmtToInsertBefore) {
+ if (value.getType() != BasicType.INT && value.getType() != BasicType.UINT) {
+ return null;
+ }
+ // If the given fact manager is at global scope, the caller expects a expression generated by
+ // this method to be available at the global scope. However the new for loop statement
+ // generated by this method will be injected into the body of a given function making it
+ // available only in the local scope. Hence, if the global scope expression is requested,
+ // null is returned.
+ if (factManager.globalScope()) {
+ return null;
+ }
+
+ // Declare and initialize a zero-value variable.
+ final String varName = genVarName(value, false);
+ final VariableDeclInfo variableDeclInfo = new VariableDeclInfo(varName, null,
+ new Initializer(
+ generateExpr(factManager,
+ currentFunction,
+ stmtToInsertBefore,
+ new NumericValue((BasicType) value.getType(), Optional.of(0)))
+ ));
+ final VariablesDeclaration variablesDecl = new VariablesDeclaration(value.getType(),
+ variableDeclInfo);
+ currentFunction.getBody().insertBefore(stmtToInsertBefore,
+ new DeclarationStmt(variablesDecl));
+
+ // Decide whether the loop should be incremented or decremented for each iteration.
+ final boolean isIncrement = generator.nextBoolean();
+ // If value is unknown, we could generate and return any number.
+ int original = (int) ((NumericValue) value).getValue().orElse(generator.nextInt(INT_MAX));
+
+ // if the original value is zero, calling random wrapper will thrown an error illegal argument
+ // exception.
+ if (original < 1) {
+ return null;
+ }
+ // A random number that will be added to the variable on each iteration.
+ // We use max function here to prevent the division by zero error.
+ int divisor = Math.max(1, generator.nextInt(original));
+
+ // TODO(https://github.com/google/graphicsfuzz/issues/659): we should be able to set the max
+ // limit as we don't want to end up having a large number of iterations.
+ final int iterations = original / divisor;
+ // A left over number that will be added after for loop is executed. The binary
+ // expression responsible to add a remainder to the variable will be inserted after for
+ // loop statement.
+ final int remainder = original % divisor;
+
+ // Values of numbers which will be used for the for loop.
+ final Value divisorValue = new NumericValue((BasicType) value.getType(), Optional.of(divisor));
+ final Value iterationValue = new NumericValue(BasicType.INT,
+ Optional.of(isIncrement ? 0 : iterations));
+ final Value conditionValue = new NumericValue(BasicType.INT,
+ Optional.of(isIncrement ? iterations : 0));
+
+ final Stmt init = new DeclarationStmt(new VariablesDeclaration(BasicType.INT,
+ new VariableDeclInfo("i", null,
+ new Initializer(generateExpr(factManager, currentFunction, stmtToInsertBefore,
+ iterationValue))))
+ );
+ final Expr condition = new BinaryExpr(new VariableIdentifierExpr("i"),
+ generateExpr(factManager, currentFunction, stmtToInsertBefore, conditionValue),
+ isIncrement ? BinOp.LT : BinOp.GE);
+ final Expr increment = new UnaryExpr(new VariableIdentifierExpr("i"),
+ isIncrement ? UnOp.POST_INC : UnOp.POST_DEC);
+ final Stmt body = new BlockStmt(Arrays.asList(new ExprStmt(new BinaryExpr(
+ new VariableIdentifierExpr(varName),
+ generateExpr(
+ factManager,
+ currentFunction,
+ stmtToInsertBefore,
+ divisorValue),
+ BinOp.ADD_ASSIGN
+ ))), false);
+ final ForStmt forStmt = new ForStmt(init, condition, increment, body);
+ currentFunction.getBody().insertBefore(stmtToInsertBefore, forStmt);
+
+ if (remainder > 0) {
+ final Value remainderValue = new NumericValue((BasicType) value.getType(),
+ Optional.of(remainder));
+ currentFunction.getBody().insertBefore(stmtToInsertBefore, new ExprStmt(new BinaryExpr(
+ new VariableIdentifierExpr(varName),
+ generateExpr(factManager, currentFunction, stmtToInsertBefore, remainderValue),
+ BinOp.ADD_ASSIGN
+ )));
+ }
+ final VariableDeclFact variableDeclFact = new VariableDeclFact(variablesDecl,
+ variableDeclInfo, value);
+ factManager.addVariableFact(value, variableDeclFact);
+ return new VariableIdentifierExpr(varName);
+ }
+
+ // A utility method performing the subtraction of the interface Number.
+ private Number subtractNumbers(Number firstOperand, Number secondOperand) {
+ if (firstOperand instanceof Float) {
+ assert secondOperand instanceof Float;
+ return firstOperand.floatValue() - secondOperand.floatValue();
+ }
+ assert firstOperand instanceof Integer && secondOperand instanceof Integer;
+ return firstOperand.intValue() - secondOperand.intValue();
+ }
+
+ /**
+ * This method generates the expression that performs the addition of two values which result is
+ * equal to the given value.
+ *
+ * @param value the value that will be computed by the expression generated by
+ * this method.
+ * @param factManager manager class holding the value and its associated expression that
+ * guarantees to compute the given value.
+ * @param currentFunction a function into which the new expression will be injected.
+ * @param stmtToInsertBefore statement in the body of the given function where the generated
+ * expression will be inserted before.
+ * @return a binary expression performing the addition of two numbers whose sum is equal to Value.
+ */
+ private Expr generateAdditionValue(Value value,
+ FactManager factManager,
+ FunctionDefinition currentFunction,
+ Stmt stmtToInsertBefore) {
+ if (!(value instanceof NumericValue)) {
+ return null;
+ }
+ if (value.getType() != BasicType.INT && value.getType() != BasicType.FLOAT
+ && value.getType() != BasicType.UINT) {
+ return null;
+ }
+ // If the given value is unknown, we are free to choose any arbitrary numbers for addition.
+ if (value.valueIsUnknown()) {
+ return new BinaryExpr(
+ generateExpr(factManager, currentFunction, stmtToInsertBefore,
+ new NumericValue((BasicType) value.getType(), Optional.empty())),
+ generateExpr(factManager, currentFunction, stmtToInsertBefore,
+ new NumericValue((BasicType) value.getType(), Optional.empty())),
+ BinOp.ADD);
+ }
+
+ final Number expected = ((NumericValue) value).getValue().get();
+ Number summandA = null;
+ // Given the expected type, we have to retrieve all values from the fact manager and filter only
+ // the facts that are known to compute particular values.
+ final List knownValues = factManager.getValuesFromType(value.getType())
+ .stream().filter(item -> !item.valueIsUnknown()).collect(Collectors.toList());
+ boolean genSummandsFromKnownValues = generator.nextBoolean();
+ // If we are able to find any known values with the correct type, we then consider choosing
+ // summands based on such values. Otherwise, we will have to pick a random number to make an
+ // addition expression.
+ if (!knownValues.isEmpty() && genSummandsFromKnownValues) {
+ summandA = ((NumericValue) knownValues
+ .get(generator.nextInt(knownValues.size())))
+ .getValue().get();
+ } else {
+ if (value.getType() == BasicType.FLOAT) {
+ // TODO(https://github.com/google/graphicsfuzz/issues/688): range of numbers [-10, 10] is
+ // temporarily used here, we have to change how the summand is generated.
+ summandA = (float) generator.nextInt(21) - 10;
+ } else if (value.getType() == BasicType.UINT) {
+ // We pick a random number in a range of numbers [1- expectedValue] so that when
+ // subtracting the random summand with the expected value we would get the non-negative
+ // result.
+ summandA = generator.nextInt(Math.max(1, expected.intValue()));
+ } else if (value.getType() == BasicType.INT) {
+ summandA = generator.nextInt(INT_MAX);
+ }
+ }
+
+ assert summandA != null;
+ // To get the second summand, we subtract original value by the the first summand.
+ final Number summandB = subtractNumbers(expected, summandA);
+ // We have a chance to have a summandA based on known fact that is greater than the expected
+ // value making the summandB obtained by subtraction negative. We thus have to ensure that
+ // the result of subtraction is non-negative number as since it is not legal to have singed
+ // number for uint type.
+ if (value.getType() == BasicType.UINT && String.valueOf(summandB).contains("-")) {
+ return null;
+ }
+ // Randomly decide whether summandA or summandB should be the first summand.
+ final boolean summandAFirst = generator.nextBoolean();
+ final Number firstSummand = summandAFirst ? summandA : summandB;
+ final Number secondSummand = summandAFirst ? summandB : summandA;
+
+ return new BinaryExpr(
+ generateExpr(factManager, currentFunction, stmtToInsertBefore,
+ new NumericValue((BasicType) value.getType(), Optional.of(firstSummand))),
+ generateExpr(factManager, currentFunction, stmtToInsertBefore,
+ new NumericValue((BasicType) value.getType(), Optional.of(secondSummand))),
+ BinOp.ADD);
+ }
+
+ /**
+ * Retrieves known value from the function facts hold by fact manager and returns the
+ * function call to that function with the appropriate parameters if there is a function fact that
+ * represents the given value.
+ *
+ * @param value the value that will be computed by the expression generated by
+ * this method.
+ * @param factManager manager class holding the value and its associated expression that
+ * guarantees to compute the given value.
+ * @param currentFunction a function into which the new expression will be injected.
+ * @param stmtToInsertBefore statement in the body of the given function where the generated
+ * expression will be inserted before.
+ * @return a function call expression to the already declared function representing the Value.
+ */
+ private Expr generateKnownFunctionFact(FactManager factManager,
+ Value value,
+ FunctionDefinition currentFunction,
+ Stmt stmtToInsertBefore) {
+ final List availableFacts = factManager.getFunctionFacts(value);
+ if (availableFacts.isEmpty()) {
+ return null;
+ }
+ final FunctionFact functionFact = availableFacts.get(generator.nextInt(availableFacts.size()));
+ final List argValues = functionFact.getArguments();
+ final List args = generateArgsForFunctionCall(factManager, currentFunction,
+ stmtToInsertBefore,
+ argValues);
+ return new FunctionCallExpr(functionFact.getFunctionName(), args);
+ }
+
+ /**
+ * Retrieves known value from the variable facts hold by fact manager and returns the
+ * variable identifier to that variable if there is a variable fact that represents the given
+ * value.
+ *
+ * @param value the value that will be computed by the expression generated by
+ * this method.
+ * @param factManager manager class holding the value and its associated expression that
+ * guarantees to compute the given value.
+ * @return a variable identifier expression to the already declared variable representing value.
+ */
+ private Expr generateKnownVariableFact(Value value, FactManager factManager) {
+ final List availableFacts = factManager.getVariableFacts(value);
+ if (availableFacts.isEmpty()) {
+ return null;
+ }
+ final VariableFact variableFact = availableFacts.get(generator.nextInt(availableFacts.size()));
+ return new VariableIdentifierExpr(variableFact.getVariableName());
+ }
+
+ /**
+ * Utility function to generate a set of expressions used as the arguments of the function call
+ * expression.
+ *
+ * @param factManager manager class holding the value and its associated expression that
+ * guarantees to compute the given value.
+ * @param functionDefinition a function into which the new expression will be injected.
+ * @param stmtToInsertBefore statement in the body of the given function where the generated
+ * expression will be inserted before.
+ * @param argValues values of function arguments from which the new values being
+ * generated are derived.
+ * @return a list of function argument expressions.
+ */
+ private List generateArgsForFunctionCall(FactManager factManager,
+ FunctionDefinition functionDefinition,
+ Stmt stmtToInsertBefore,
+ List argValues) {
+ return argValues.stream()
+ .map(item -> generateExpr(
+ factManager,
+ functionDefinition,
+ stmtToInsertBefore,
+ item
+ )).collect(Collectors.toList());
+ }
+
+ private String freshId() {
+ return ID + "_" + idGenerator.freshId();
+ }
+
+ private String genVarName(Value value, boolean isGlobal) {
+ // Provides name for a new variable based on the given value.
+ // For example:
+ // _GLF_PRIMITIVE_int_negative_103_0_id_18: a variable of a value -103.
+ // _GLF_PRIMITIVE_float_unknown_numeric_id_69: a variable that can be any value of float type.
+ return (isGlobal ? Constants.GLF_PRIMITIVE_GLOBAL : Constants.GLF_PRIMITIVE)
+ + "_" + value.getType().toString()
+ + parseNameFromValue(value)
+ + freshId();
+ }
+
+ private String genFunctionName(Value value) {
+ // Provides name for a new function based on the given value.
+ // For example:
+ // _GLF_COMPUTE_float_1_0_id_0: a function that returns a value 1.0 of float type.
+ // _GLF_COMPUTE_vec4_UNKNOWN_id_1: a function that returns randomly generated value of vec4
+ // type.
+ return Constants.GLF_COMPUTE
+ + "_" + value.getType().toString()
+ + parseNameFromValue(value)
+ + freshId();
+ }
+
+ private String genParamName(Value value) {
+ // Provides name for a function arguments based on the given value.
+ // For example:
+ // _GLF_UNKNOWN_PARAM_vec4_id_1: a parameter of unknown value of vec4 type.
+ // _GLF_PARAM_int_id_62: a parameter of integer type.
+ return (value.valueIsUnknown() ? Constants.GLF_UNKNOWN_PARAM : Constants.GLF_PARAM)
+ + "_" + value.getType().toString()
+ + parseNameFromValue(value)
+ + freshId();
+ }
+
+ /**
+ * Utility function to parse name from the given value, for example, -0.45 will be parsed as
+ * negative_0_45.
+ *
+ * @param value value that will be converted to the name of variables or functions.
+ * @return a string derived from the given value which will be used as function or variable names.
+ */
+ private String parseNameFromValue(Value value) {
+ if (value.valueIsUnknown()) {
+ return "_" + value.toString();
+ }
+ final StringBuilder name = new StringBuilder();
+ if (value instanceof NumericValue) {
+ final NumericValue numericValue = (NumericValue) value;
+ float floatValue = numericValue.getValue().get().floatValue();
+ if (String.valueOf(floatValue).contains("-")) {
+ name.append(NEGATIVE);
+ floatValue = Math.abs(floatValue);
+ }
+ name.append("_");
+ // Replace dot with underscore, i.e., 0.45 will be converted to 0_45.
+ name.append(Float.toString(floatValue).replace(".", "_"));
+
+ }
+ if (value instanceof BooleanValue) {
+ name.append("_").append(value.toString());
+ }
+ return name.toString();
+ }
+
+ /**
+ * Generates a new variable to which the expected value is assigned. The variable being
+ * generated is also randomly chosen whether it would be available at the local or global scope.
+ *
+ * @param value the value that will be computed by the expression generated by
+ * this method.
+ * @param factManager manager class holding the value and its associated expression that
+ * guarantees to compute the given value.
+ * @param currentFunction a function into which the new expression will be injected.
+ * @param stmtToInsertBefore statement in the body of the given function where the generated
+ * expression will be inserted before.*
+ * @return the variable identifier expression of the declared variable generated by this method.
+ */
+ private Expr generateVariableFact(Value value,
+ FactManager factManager,
+ FunctionDefinition currentFunction,
+ Stmt stmtToInsertBefore) {
+ boolean atGlobalScope = factManager.globalScope() || generator.nextBoolean();
+ final String varName = genVarName(value, atGlobalScope);
+ final Expr initializer = generateExpr(atGlobalScope ? globalFactManager : factManager,
+ currentFunction, stmtToInsertBefore, value);
+ // If generating global scope variables, initialisation would be performed later in a global
+ // initializer function.
+ final VariableDeclInfo variableDeclInfo = new VariableDeclInfo(varName, null,
+ atGlobalScope ? null : new Initializer(initializer));
+ final VariablesDeclaration variablesDecl = new VariablesDeclaration(value.getType(),
+ variableDeclInfo);
+ final VariableDeclFact variableDeclFact = new VariableDeclFact(variablesDecl,
+ variableDeclInfo, value);
+
+ if (atGlobalScope) {
+ // Search for an existing function for initialization of global variables. If not found, we
+ // have to create one and add a call to the top of 'main' to invoke it.
+ Optional maybeInitGlobalsFunction =
+ translationUnit.getTopLevelDeclarations()
+ .stream()
+ .filter(item -> item instanceof FunctionDefinition)
+ .map(item -> (FunctionDefinition) item)
+ .filter(item -> item.getPrototype().getName().equals(Constants.GLF_INIT_GLOBALS))
+ .findFirst();
+
+ FunctionDefinition initGlobalsFunction;
+ if (maybeInitGlobalsFunction.isPresent()) {
+ initGlobalsFunction = maybeInitGlobalsFunction.get();
+ } else {
+ // Function prototype of the globals initializer, this must be declared at the top level of
+ // the shader being generated.
+ final FunctionPrototype functionPrototype =
+ new FunctionPrototype(Constants.GLF_INIT_GLOBALS, VoidType.VOID,
+ new ArrayList<>());
+ translationUnit.addDeclarationBefore(functionPrototype, translationUnit.getMainFunction());
+
+ // The invocation to the globals initializer which needs to be inserted as the first
+ // statement in main function.
+ translationUnit.getMainFunction().getBody().insertStmt(0,
+ new ExprStmt(new FunctionCallExpr(Constants.GLF_INIT_GLOBALS, new ArrayList<>())));
+
+ // A function into which the assignments of values for global variables are injected.
+ initGlobalsFunction = new FunctionDefinition(functionPrototype,
+ new BlockStmt(new ArrayList<>(), false));
+ final List newTopLevelDeclarations = new ArrayList<>();
+ newTopLevelDeclarations.addAll(translationUnit.getTopLevelDeclarations());
+ newTopLevelDeclarations.add(initGlobalsFunction);
+ translationUnit.setTopLevelDeclarations(newTopLevelDeclarations);
+ }
+
+ // Inject a statement assigning value to the global variable into function body.
+ initGlobalsFunction.getBody().addStmt(new ExprStmt(new BinaryExpr(
+ new VariableIdentifierExpr(varName), initializer, BinOp.ASSIGN
+ )));
+
+ translationUnit.addDeclarationBefore(variablesDecl, currentFunction);
+ globalFactManager.addVariableFact(value, variableDeclFact);
+ } else {
+ currentFunction.getBody().insertBefore(stmtToInsertBefore,
+ new DeclarationStmt(variablesDecl));
+ factManager.addVariableFact(value, variableDeclFact);
+ }
+ return new VariableIdentifierExpr(varName);
+ }
+
+ /**
+ * Generates a new function that guarantees to return the value representing the expected Value.
+ * A number and type of the function arguments for the function being generated are randomized.
+ *
+ * @param value the value that will be computed by the expression generated by
+ * this method.
+ * @param factManager manager class holding the value and its associated expression that
+ * guarantees to compute the given value.
+ * @param currentFunction a function into which the new expression will be injected.
+ * @param stmtToInsertBefore statement in the body of the given function where the generated
+ * expression will be inserted before.
+ * @return function call expression of the function generated by this method.
+ */
+ private Expr generateFunctionFact(Value value,
+ FactManager factManager,
+ FunctionDefinition currentFunction,
+ Stmt stmtToInsertBefore) {
+ final String functionName = genFunctionName(value);
+ final FactManager newFunctionScope = globalFactManager.newScope();
+ final List argumentValues = new ArrayList<>();
+ final List parameterDecls = new ArrayList<>();
+
+ final int noOfParams = generator.nextInt(MAX_FUNCTION_PARAMS);
+ for (int i = 0; i < noOfParams; i++) {
+ final Type paramType = getAvailableTypes().get(generator.nextInt(getAvailableTypes().size()));
+ // Decide whether the value generated should be known by the fact manager.
+ // If the fact manager is generating an unknown parameter(value is Optional.empty),
+ // when calling this function the fact manager will generate any arbitrary value that
+ // matches the parameter type.
+ final Value paramValue = fuzzValue(paramType, false);
+ final String paramName = genParamName(paramValue);
+
+ argumentValues.add(paramValue);
+ final ParameterDecl parameterDecl = new ParameterDecl(
+ paramName,
+ paramType,
+ null
+ );
+ parameterDecls.add(parameterDecl);
+ newFunctionScope.addVariableFact(paramValue, new ParameterDeclFact(parameterDecl,
+ paramValue));
+ }
+ final BlockStmt body = new BlockStmt(Collections.emptyList(), false);
+ final FunctionPrototype functionPrototype = new FunctionPrototype(functionName,
+ value.getType(), parameterDecls);
+ final FunctionDefinition newFunction = new FunctionDefinition(functionPrototype,
+ body);
+ translationUnit.addDeclarationBefore(newFunction, currentFunction);
+
+ // Since the new function has an empty body, we first need to add a placeholder statement
+ // into the body to be the point in a function where the new expressions can be injected
+ // before.
+ final Stmt placeholderStmt = new NullStmt();
+ body.addStmt(placeholderStmt);
+
+ // We then replace the placeholder statement with a new return statement, returning a
+ // newly-generated expression.
+ body.replaceChild(placeholderStmt, new ReturnStmt(
+ generateExpr(
+ newFunctionScope,
+ newFunction,
+ placeholderStmt,
+ value
+ )
+ ));
+
+ globalFactManager.addFunctionFact(value, new FunctionFact(functionPrototype, argumentValues,
+ value));
+ final List args = generateArgsForFunctionCall(factManager, currentFunction,
+ stmtToInsertBefore,
+ argumentValues);
+ return new FunctionCallExpr(functionName, args);
+ }
+
+ private List extends Type> getAvailableTypes() {
+ // We will have to consider supporting more types but at the moment as we are using
+ // LiteralFuzzer to generate literal expressions so we are unable to cover all basic type now.
+ // The following are types currently supported by LiteralFuzzer.
+ return Arrays.asList(BasicType.BOOL, BasicType.INT, BasicType.UINT, BasicType.FLOAT,
+ BasicType.VEC2, BasicType.VEC3, BasicType.VEC4);
+ }
+
+ private Value fuzzValue(Type type, boolean forceGenerateKnownValue) {
+ // An Unknown value variable is the variable whose value could be anything. The fact manager
+ // could generate any arbitrary value but with the correct type.
+ final boolean isUnknown = generator.nextBoolean() && !forceGenerateKnownValue;
+ if (type == BasicType.BOOL) {
+ return new BooleanValue(
+ isUnknown ? Optional.empty() : Optional.of(generator.nextBoolean()));
+ }
+ if (BasicType.allScalarTypes().contains(type)) {
+ if (isUnknown) {
+ return new NumericValue((BasicType) type, Optional.empty());
+ }
+ if (type == BasicType.INT) {
+ final String intString = String.valueOf(generator.nextInt(INT_MAX - INT_MIN) + INT_MIN);
+ return new NumericValue(BasicType.INT, Optional.of(Integer.valueOf(intString)));
+ }
+ if (type == BasicType.FLOAT) {
+ final String floatString = LiteralFuzzer.randomFloatString(generator);
+ return new NumericValue(BasicType.FLOAT, Optional.of(Float.valueOf(floatString)));
+ }
+ if (type == BasicType.UINT) {
+ final String intString = String.valueOf(generator.nextInt(INT_MAX - INT_MIN) + INT_MIN);
+ return new NumericValue(BasicType.UINT, Optional.of(Math.abs(Integer.valueOf(intString))));
+ }
+ throw new RuntimeException("Not implemented yet!");
+ }
+
+ if (isUnknown) {
+ return new CompositeValue(type, Optional.empty());
+ }
+ if (BasicType.allBasicTypes().contains(type)) {
+ final List values = new ArrayList<>();
+ for (int i = 0; i < ((BasicType) type).getNumElements(); i++) {
+ values.add(fuzzValue(((BasicType) type).getElementType(), forceGenerateKnownValue));
+ }
+ return new CompositeValue(type, Optional.of(values));
+ }
+
+ // TODO(https://github.com/google/graphicsfuzz/issues/664): we should also support array and
+ // struct types as well.
+ throw new RuntimeException("Not implemented yet!");
+ }
+
+}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/FactManager.java b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/FactManager.java
new file mode 100644
index 000000000..e0565a12c
--- /dev/null
+++ b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/FactManager.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2019 The GraphicsFuzz Project Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphicsfuzz.generator.knownvaluegeneration;
+
+import com.graphicsfuzz.common.ast.type.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class FactManager {
+
+ // A parent fact manager whose facts this fact manager extends.
+ private final FactManager prototype;
+ private final Map> variableFacts;
+ private final Map> functionFacts;
+
+ public FactManager(FactManager prototype) {
+ this.prototype = prototype;
+ variableFacts = new HashMap>();
+ functionFacts = new HashMap>();
+ }
+
+ /**
+ * Given an expected type, this method gets available function and variable facts hold by
+ * this fact manager.
+ *
+ * @param type type of the facts we are going to search for.
+ * @return a list of values that matches the given type.
+ */
+ public List getValuesFromType(Type type) {
+ return Stream.concat(variableFacts.keySet().stream(), functionFacts.keySet().stream())
+ .filter(value -> value.getType() == type)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Creates a new fact manager where this fact manager is used as a prototype. We basically call
+ * this method only when generating a new function where the return fact manager is non global.
+ *
+ * @return new fact manager.
+ */
+ public FactManager newScope() {
+ return new FactManager(this);
+ }
+
+ /**
+ * Determines whether the fact manager is at the global scope.
+ *
+ * @return true if this manager has a prototype and false otherwise.
+ */
+ public boolean globalScope() {
+ return prototype == null;
+ }
+
+ /**
+ * Adds a new variable fact to the variable fact map of this fact manager.
+ *
+ * @param value value which the given variable fact is representing.
+ * @param variableFact a new variable fact that will be added into the map of variable facts.
+ */
+ public void addVariableFact(Value value, VariableFact variableFact) {
+ final List newFacts = variableFacts.getOrDefault(value, new ArrayList<>());
+ newFacts.add(variableFact);
+ variableFacts.put(value, newFacts);
+ }
+
+ /**
+ * As function facts can only exist at the global scope, this method adds a new function
+ * fact into the map of the function facts of the global scope fact manager.
+ *
+ * @param value value which the given function fact is representing.
+ * @param functionFact a new function fact that will be added into the map of function facts.
+ */
+ public void addFunctionFact(Value value, FunctionFact functionFact) {
+ if (globalScope()) {
+ final List newFacts = functionFacts.getOrDefault(value, new ArrayList<>());
+ newFacts.add(functionFact);
+ functionFacts.put(value, newFacts);
+ } else {
+ prototype.addFunctionFact(value, functionFact);
+ }
+ }
+
+ /**
+ * Retrieves a list of variable facts representing the value.
+ *
+ * @param value a value which the fact manager is asked to search in the variable fact map.
+ * @return if value does not exist in the variable fact map, an empty list is returned. Otherwise,
+ * returns a list of variable facts that guarantees to provide the given value.
+ */
+ public List getVariableFacts(Value value) {
+ final List result = new ArrayList<>();
+ result.addAll(variableFacts.getOrDefault(value, Collections.emptyList()));
+ if (!globalScope()) {
+ result.addAll(prototype.getVariableFacts(value));
+ }
+ return result;
+ }
+
+ /**
+ * Retrieves a list of function facts representing the value from the global scope fact
+ * manager. If the value does not exist in the map, an empty list is returned.
+ *
+ * @param value a value which the fact manager is asked to search in the function fact map.
+ * @return if value does not exist in the function fact map, an empty list is returned.
+ * Otherwise, returns a list of function facts that guarantees to provide the given value.
+ */
+ public List getFunctionFacts(Value value) {
+ if (globalScope()) {
+ return functionFacts.getOrDefault(value, Collections.emptyList());
+ }
+ return prototype.getFunctionFacts(value);
+ }
+
+}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/FunctionFact.java b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/FunctionFact.java
new file mode 100644
index 000000000..dfbdb15a5
--- /dev/null
+++ b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/FunctionFact.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 The GraphicsFuzz Project Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphicsfuzz.generator.knownvaluegeneration;
+
+import com.graphicsfuzz.common.ast.decl.FunctionPrototype;
+import java.util.List;
+
+/**
+ * This class holds the information of the newly-generated function and its associated Value.
+ * Each time we generate a new function, we create a function fact and keep it in Fact Manager
+ * which later will be used by the Expression Generator when generating known value expression.
+ */
+public class FunctionFact {
+
+ private final FunctionPrototype prototype;
+ private final List arguments;
+ private final Value value;
+
+ public FunctionFact(FunctionPrototype prototype, List arguments, Value value) {
+ this.prototype = prototype;
+ this.arguments = arguments;
+ this.value = value;
+ }
+
+ public String getFunctionName() {
+ return prototype.getName();
+ }
+
+ public List getArguments() {
+ return arguments;
+ }
+
+ public Value getValue() {
+ return value;
+ }
+
+}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/NumericValue.java b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/NumericValue.java
new file mode 100644
index 000000000..983a8451f
--- /dev/null
+++ b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/NumericValue.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019 The GraphicsFuzz Project Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphicsfuzz.generator.knownvaluegeneration;
+
+import com.graphicsfuzz.common.ast.expr.Expr;
+import com.graphicsfuzz.common.ast.expr.FloatConstantExpr;
+import com.graphicsfuzz.common.ast.expr.IntConstantExpr;
+import com.graphicsfuzz.common.ast.expr.UIntConstantExpr;
+import com.graphicsfuzz.common.ast.type.BasicType;
+import com.graphicsfuzz.common.ast.type.Type;
+import com.graphicsfuzz.generator.semanticschanging.LiteralFuzzer;
+import java.util.Objects;
+import java.util.Optional;
+
+public class NumericValue implements Value {
+
+ private final BasicType basicType;
+ private final Optional value;
+
+ public NumericValue(BasicType basicType, Optional value) {
+ this.basicType = basicType;
+ this.value = value;
+ }
+
+ @Override
+ public Type getType() {
+ return basicType;
+ }
+
+ @Override
+ public boolean valueIsUnknown() {
+ return !value.isPresent();
+ }
+
+ @Override
+ public Expr generateLiteral(LiteralFuzzer literalFuzzer) {
+ if (valueIsUnknown()) {
+ return literalFuzzer.fuzz(basicType).orElse(null);
+ }
+ if (basicType == BasicType.FLOAT) {
+ return new FloatConstantExpr(value.get().toString());
+ }
+ if (basicType == BasicType.INT) {
+ return new IntConstantExpr(value.get().toString());
+ }
+ if (basicType == BasicType.UINT) {
+ return new UIntConstantExpr(value.get().toString() + "u");
+ }
+ throw new RuntimeException("Numeric value does not support the given type");
+ }
+
+ public Optional getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object that) {
+ if (this == that) {
+ return true;
+ }
+
+ if (!(that instanceof NumericValue)) {
+ return false;
+ }
+ final NumericValue thatNumericValue = (NumericValue) that;
+ return this.basicType.equals(thatNumericValue.basicType)
+ && this.value.equals(thatNumericValue.getValue());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(basicType, value);
+ }
+
+ @Override
+ public String toString() {
+ return valueIsUnknown() ? "unknown_numeric" : value.get().toString();
+ }
+
+}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/ParameterDeclFact.java b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/ParameterDeclFact.java
new file mode 100644
index 000000000..f990d26ab
--- /dev/null
+++ b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/ParameterDeclFact.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The GraphicsFuzz Project Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphicsfuzz.generator.knownvaluegeneration;
+
+import com.graphicsfuzz.common.ast.decl.ParameterDecl;
+
+/**
+ * This class holds the information of a parameter of generated functions and its associated value.
+ * For any generated function, we randomly decide how parameters should be and for each parameter
+ * we create a parameter declaration fact and keep it in Fact Manager which later will be used by
+ * the Expression Generator when generating known value expression.
+ */
+public class ParameterDeclFact extends VariableFact {
+
+ private final ParameterDecl parameterDecl;
+
+ public ParameterDeclFact(ParameterDecl parameterDecl,
+ Value value) {
+ super(value);
+ this.parameterDecl = parameterDecl;
+ }
+
+ @Override
+ public String getVariableName() {
+ return parameterDecl.getName();
+ }
+
+}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/Value.java b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/Value.java
new file mode 100644
index 000000000..da9197605
--- /dev/null
+++ b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/Value.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The GraphicsFuzz Project Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphicsfuzz.generator.knownvaluegeneration;
+
+import com.graphicsfuzz.common.ast.expr.Expr;
+import com.graphicsfuzz.common.ast.type.Type;
+import com.graphicsfuzz.generator.semanticschanging.LiteralFuzzer;
+
+/**
+ * This interface defines an expected Value used by {@link ExpressionGenerator} when
+ * generating an expression. It represents a possibly unknown value of some type.
+ */
+public interface Value {
+
+ /**
+ * Indicates whether or not this is an unknown value.
+ * @return true if the value is unknown and false otherwise.
+ */
+ boolean valueIsUnknown();
+
+ /**
+ * Gets the type of the underlying value.
+ * @return the basic type of the value.
+ */
+ Type getType();
+
+ /**
+ * Provides a literal with the same type as the Value's type, such that all parts of the value
+ * that are known will have the expected values, and all other parts will be randomized.
+ *
+ * @param literalFuzzer a util class used to generate fuzzed expressions.
+ * @return the expression that represents the value.
+ */
+ Expr generateLiteral(LiteralFuzzer literalFuzzer);
+
+}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/VariableDeclFact.java b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/VariableDeclFact.java
new file mode 100644
index 000000000..8b49f1a5c
--- /dev/null
+++ b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/VariableDeclFact.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The GraphicsFuzz Project Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphicsfuzz.generator.knownvaluegeneration;
+
+import com.graphicsfuzz.common.ast.decl.VariableDeclInfo;
+import com.graphicsfuzz.common.ast.decl.VariablesDeclaration;
+
+/**
+ * This class holds the information of the newly-generated variable and its associated Value.
+ * Each time we declare a new variable, we create a variable declaration fact and keep it in Fact
+ * Manager which later will be used by the Expression Generator when generating known value
+ * expression.
+ */
+public class VariableDeclFact extends VariableFact {
+
+ private final VariablesDeclaration variablesDeclaration;
+ private final VariableDeclInfo variableDeclInfo;
+
+ public VariableDeclFact(VariablesDeclaration variablesDeclaration,
+ VariableDeclInfo variableDeclInfo,
+ Value value) {
+ super(value);
+ this.variablesDeclaration = variablesDeclaration;
+ this.variableDeclInfo = variableDeclInfo;
+ }
+
+ public String getVariableName() {
+ return variableDeclInfo.getName();
+ }
+
+}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/VariableFact.java b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/VariableFact.java
new file mode 100644
index 000000000..62b5c1e5d
--- /dev/null
+++ b/generator/src/main/java/com/graphicsfuzz/generator/knownvaluegeneration/VariableFact.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The GraphicsFuzz Project Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphicsfuzz.generator.knownvaluegeneration;
+
+public abstract class VariableFact {
+
+ private final Value value;
+
+ /**
+ * @param value which a new VariableDeclFact or ParameterDeclFact is representing.
+ */
+ VariableFact(Value value) {
+ this.value = value;
+ }
+
+ /**
+ * @return a known value of the variable fact.
+ */
+ public Value getValue() {
+ return value;
+ }
+
+ /**
+ * @return a variable name of this variable fact. This is used by the generator
+ * when it is generating a new variable identifier expression.
+ */
+ public abstract String getVariableName();
+
+}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/mutateapi/MutationFinderBase.java b/generator/src/main/java/com/graphicsfuzz/generator/mutateapi/MutationFinderBase.java
index 5d8abfad9..a577bc996 100755
--- a/generator/src/main/java/com/graphicsfuzz/generator/mutateapi/MutationFinderBase.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/mutateapi/MutationFinderBase.java
@@ -34,15 +34,13 @@
import com.graphicsfuzz.common.ast.stmt.BlockStmt;
import com.graphicsfuzz.common.ast.stmt.ForStmt;
import com.graphicsfuzz.common.ast.stmt.Stmt;
-import com.graphicsfuzz.common.typing.ScopeTreeBuilder;
-import com.graphicsfuzz.generator.mutateapi.Mutation;
-import com.graphicsfuzz.generator.mutateapi.MutationFinder;
+import com.graphicsfuzz.common.typing.ScopeTrackingVisitor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class MutationFinderBase
- extends ScopeTreeBuilder implements MutationFinder {
+ extends ScopeTrackingVisitor implements MutationFinder {
private final TranslationUnit tu;
private final List mutations;
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/AddArrayMutation.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/AddArrayMutation.java
index 59097110a..ee052f257 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/AddArrayMutation.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/AddArrayMutation.java
@@ -20,6 +20,7 @@
import com.graphicsfuzz.common.ast.decl.ArrayInfo;
import com.graphicsfuzz.common.ast.decl.VariableDeclInfo;
import com.graphicsfuzz.common.ast.decl.VariablesDeclaration;
+import com.graphicsfuzz.common.ast.expr.IntConstantExpr;
import com.graphicsfuzz.common.ast.type.BasicType;
import com.graphicsfuzz.generator.mutateapi.Mutation;
@@ -40,7 +41,9 @@ public AddArrayMutation(TranslationUnit tu, String name, BasicType baseType, int
@Override
public void apply() {
tu.addDeclaration(new VariablesDeclaration(
- baseType, new VariableDeclInfo(name, new ArrayInfo(numElements), null)));
+ baseType, new VariableDeclInfo(name,
+ new ArrayInfo(new IntConstantExpr(Integer.toString(numElements))),
+ null)));
}
}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/Expr2ArrayAccessMutationFinder.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/Expr2ArrayAccessMutationFinder.java
index 66b610f64..744274264 100755
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/Expr2ArrayAccessMutationFinder.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/Expr2ArrayAccessMutationFinder.java
@@ -52,7 +52,7 @@ protected void visitVariableDeclInfoAfterAddedToScope(VariableDeclInfo declInfo)
if (!atGlobalScope()) {
return;
}
- ScopeEntry se = currentScope.lookupScopeEntry(declInfo.getName());
+ ScopeEntry se = getCurrentScope().lookupScopeEntry(declInfo.getName());
if (se.getType() instanceof ArrayType) {
globalArrays.put(declInfo.getName(), ((ArrayType) se.getType()).getBaseType()
.getWithoutQualifiers());
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/InterchangeExprMutationFinder.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/InterchangeExprMutationFinder.java
index 2680d1a6d..3ce173047 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/InterchangeExprMutationFinder.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/InterchangeExprMutationFinder.java
@@ -63,7 +63,8 @@ protected void visitExpr(Expr expr) {
private Expr applyInterchange(Expr expr, List argumentTypes) {
final Type resultType = typer.lookupType(expr).getWithoutQualifiers();
List templates = Fuzzer.availableTemplatesFromScope(
- getTranslationUnit().getShadingLanguageVersion(), currentScope)
+ getTranslationUnit().getShadingLanguageVersion(), getTranslationUnit().getShaderKind(),
+ getCurrentScope())
// Ignore templates that require l-values, so that invalidity is not too likely
.filter(InterchangeExprMutationFinder::doesNotRequireLvalue)
// Ignore the possibility of replacing a one-argument expression with parentheses, as it
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/LiteralFuzzer.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/LiteralFuzzer.java
index 8a6c46de2..c97bbeb11 100755
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/LiteralFuzzer.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/LiteralFuzzer.java
@@ -21,6 +21,7 @@
import com.graphicsfuzz.common.ast.expr.FloatConstantExpr;
import com.graphicsfuzz.common.ast.expr.IntConstantExpr;
import com.graphicsfuzz.common.ast.expr.TypeConstructorExpr;
+import com.graphicsfuzz.common.ast.expr.UIntConstantExpr;
import com.graphicsfuzz.common.ast.type.BasicType;
import com.graphicsfuzz.common.ast.type.Type;
import com.graphicsfuzz.common.util.IRandom;
@@ -47,14 +48,17 @@ public Optional fuzz(Type type) {
}
if (type == BasicType.INT) {
return Optional.of(new IntConstantExpr(
- String.valueOf(generator.nextInt(INT_MAX - INT_MIN) + INT_MIN)));
+ String.valueOf(generator.nextInt(INT_MAX - INT_MIN) + INT_MIN)));
+ }
+ if (type == BasicType.UINT) {
+ return Optional.of(new UIntConstantExpr(
+ String.valueOf(Math.abs(generator.nextInt(INT_MAX - INT_MIN) + INT_MIN) + "u")));
}
if (type == BasicType.FLOAT) {
- return Optional.of(new FloatConstantExpr(
- randomFloatString()));
+ return Optional.of(new FloatConstantExpr(LiteralFuzzer.randomFloatString(generator)));
}
if (type == BasicType.VEC2 || type == BasicType.VEC3 || type == BasicType.VEC4
- || BasicType.allMatrixTypes().contains(type)) {
+ || BasicType.allMatrixTypes().contains(type)) {
final List args = new ArrayList<>();
for (int i = 0; i < ((BasicType) type).getNumElements(); i++) {
args.add(fuzz(((BasicType) type).getElementType()).get());
@@ -64,7 +68,7 @@ public Optional fuzz(Type type) {
return Optional.empty();
}
- private String randomFloatString() {
+ public static String randomFloatString(IRandom generator) {
final int maxDigitsEitherSide = 5;
StringBuilder sb = new StringBuilder();
sb.append(generator.nextBoolean() ? "-" : "");
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/ReplaceBlockStmtsWithSwitchMutationFinder.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/ReplaceBlockStmtsWithSwitchMutationFinder.java
index 04543c9f3..6cf21f153 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/ReplaceBlockStmtsWithSwitchMutationFinder.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/ReplaceBlockStmtsWithSwitchMutationFinder.java
@@ -128,8 +128,9 @@ public void visitBlockStmt(BlockStmt block) {
private Expr getSwitchCondition() {
final List candidateVariables = new ArrayList<>();
- currentScope.namesOfAllVariablesInScope().stream()
- .filter(item -> currentScope.lookupType(item).getWithoutQualifiers().equals(BasicType.INT))
+ getCurrentScope().namesOfAllVariablesInScope().stream()
+ .filter(item -> getCurrentScope().lookupType(item).getWithoutQualifiers()
+ .equals(BasicType.INT))
.forEach(candidateVariables::add);
if (candidateVariables.isEmpty()) {
return new LiteralFuzzer(generator).fuzz(BasicType.INT).get();
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/SwapVariableIdentifiersMutationFinder.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/SwapVariableIdentifiersMutationFinder.java
index 8ab4d39c2..d86574934 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/SwapVariableIdentifiersMutationFinder.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticschanging/SwapVariableIdentifiersMutationFinder.java
@@ -44,11 +44,12 @@ public void visitVariableIdentifierExpr(VariableIdentifierExpr variableIdentifie
return;
}
- if (currentScope.lookupType(variableIdentifierExpr.getName()) == null) {
+ if (getCurrentScope().lookupType(variableIdentifierExpr.getName()) == null) {
return;
}
- assert currentScope.lookupType(variableIdentifierExpr.getName()).getWithoutQualifiers() != null;
+ assert getCurrentScope().lookupType(variableIdentifierExpr.getName()).getWithoutQualifiers()
+ != null;
final List candidateVariables = getCandidateVariables(variableIdentifierExpr.getName());
@@ -65,11 +66,11 @@ public void visitVariableIdentifierExpr(VariableIdentifierExpr variableIdentifie
}
private List getCandidateVariables(String varIdentifier) {
- return currentScope.namesOfAllVariablesInScope().stream()
+ return getCurrentScope().namesOfAllVariablesInScope().stream()
.filter(item -> !item.equals(varIdentifier)
- && currentScope.lookupType(item) != null
- && currentScope.lookupType(varIdentifier).getWithoutQualifiers().equals(
- currentScope.lookupType(item).getWithoutQualifiers()))
+ && getCurrentScope().lookupType(item) != null
+ && getCurrentScope().lookupType(varIdentifier).getWithoutQualifiers().equals(
+ getCurrentScope().lookupType(item).getWithoutQualifiers()))
.collect(Collectors.toList());
}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/AddLiveOutputWriteMutation.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/AddLiveOutputWriteMutation.java
index 4533afd39..4697e91db 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/AddLiveOutputWriteMutation.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/AddLiveOutputWriteMutation.java
@@ -30,6 +30,7 @@
import com.graphicsfuzz.common.ast.type.Type;
import com.graphicsfuzz.common.glslversion.ShadingLanguageVersion;
import com.graphicsfuzz.common.util.IRandom;
+import com.graphicsfuzz.common.util.ShaderKind;
import com.graphicsfuzz.generator.fuzzer.Fuzzer;
import com.graphicsfuzz.generator.fuzzer.FuzzingContext;
import com.graphicsfuzz.generator.fuzzer.OpaqueExpressionGenerator;
@@ -63,7 +64,8 @@ outputVariableType, new VariableDeclInfo(backupName,
null, null))));
stmts.add(new ExprStmt(new BinaryExpr(new VariableIdentifierExpr(backupName),
new VariableIdentifierExpr(outputVariableName), BinOp.ASSIGN)));
- final Fuzzer fuzzer = new Fuzzer(new FuzzingContext(), shadingLanguageVersion, random,
+ final Fuzzer fuzzer = new Fuzzer(new FuzzingContext(), shadingLanguageVersion,
+ random,
generationParams);
stmts.add(new ExprStmt(new BinaryExpr(
new VariableIdentifierExpr(outputVariableName),
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/AddWrappingConditionalMutation.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/AddWrappingConditionalMutation.java
index 6c5766b14..aae749311 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/AddWrappingConditionalMutation.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/AddWrappingConditionalMutation.java
@@ -16,7 +16,7 @@
package com.graphicsfuzz.generator.semanticspreserving;
-import com.graphicsfuzz.common.ast.decl.ScalarInitializer;
+import com.graphicsfuzz.common.ast.decl.Initializer;
import com.graphicsfuzz.common.ast.decl.VariableDeclInfo;
import com.graphicsfuzz.common.ast.decl.VariablesDeclaration;
import com.graphicsfuzz.common.ast.expr.BinOp;
@@ -38,7 +38,6 @@
import com.graphicsfuzz.common.util.ContainsTopLevelBreak;
import com.graphicsfuzz.common.util.ContainsTopLevelContinue;
import com.graphicsfuzz.common.util.IRandom;
-import com.graphicsfuzz.common.util.IdGenerator;
import com.graphicsfuzz.generator.fuzzer.Fuzzer;
import com.graphicsfuzz.generator.fuzzer.FuzzingContext;
import com.graphicsfuzz.generator.fuzzer.OpaqueExpressionGenerator;
@@ -167,7 +166,7 @@ private Stmt makeSingleIterationForStmt(Stmt stmt,
: opaqueExpressionGenerator
.makeOpaqueOne(BasicType.INT, loopBoundsMustBeConst, 0, fuzzer);
DeclarationStmt init = new DeclarationStmt(new VariablesDeclaration(BasicType.INT,
- new VariableDeclInfo(loopVariableName, null, new ScalarInitializer(start))));
+ new VariableDeclInfo(loopVariableName, null, new Initializer(start))));
Expr end = up ? opaqueExpressionGenerator
.makeOpaqueOne(BasicType.INT, loopBoundsMustBeConst, 0, fuzzer)
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/IdentityMutationFinder.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/IdentityMutationFinder.java
index ff48e76ff..2a9f84724 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/IdentityMutationFinder.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/IdentityMutationFinder.java
@@ -17,9 +17,8 @@
package com.graphicsfuzz.generator.semanticspreserving;
import com.graphicsfuzz.common.ast.TranslationUnit;
-import com.graphicsfuzz.common.ast.decl.ArrayInitializer;
import com.graphicsfuzz.common.ast.decl.FunctionPrototype;
-import com.graphicsfuzz.common.ast.decl.ScalarInitializer;
+import com.graphicsfuzz.common.ast.decl.Initializer;
import com.graphicsfuzz.common.ast.decl.VariablesDeclaration;
import com.graphicsfuzz.common.ast.expr.ArrayIndexExpr;
import com.graphicsfuzz.common.ast.expr.BinaryExpr;
@@ -77,14 +76,13 @@ protected void visitExpr(Expr expr) {
}
final BasicType basicType = (BasicType) type;
- final Scope clonedScope = currentScope.shallowClone();
+ final Scope clonedScope = getCurrentScope().shallowClone();
if (getTranslationUnit().getShadingLanguageVersion().restrictedForLoops()) {
for (Set iterators : forLoopIterators) {
iterators.forEach(clonedScope::remove);
}
}
- if (BasicType.allScalarTypes().contains(basicType)
- || BasicType.allVectorTypes().contains(basicType)
+ if (basicType.isScalar() || basicType.isVector()
|| BasicType.allSquareMatrixTypes().contains(basicType)) {
// TODO: add support for non-square matrices.
addMutation(new Expr2ExprMutation(parentMap.getParent(expr),
@@ -229,18 +227,10 @@ public void visitVariablesDeclaration(VariablesDeclaration variablesDeclaration)
}
@Override
- public void visitScalarInitializer(ScalarInitializer scalarInitializer) {
+ public void visitInitializer(Initializer initializer) {
assert !inInitializer;
inInitializer = true;
- super.visitScalarInitializer(scalarInitializer);
- inInitializer = false;
- }
-
- @Override
- public void visitArrayInitializer(ArrayInitializer arrayInitializer) {
- assert !inInitializer;
- inInitializer = true;
- super.visitArrayInitializer(arrayInitializer);
+ super.visitInitializer(initializer);
inInitializer = false;
}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/OutlineStatementMutationFinder.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/OutlineStatementMutationFinder.java
index d62bf5306..c2634dd32 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/OutlineStatementMutationFinder.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/OutlineStatementMutationFinder.java
@@ -27,19 +27,11 @@
import com.graphicsfuzz.common.ast.visitors.CheckPredicateVisitor;
import com.graphicsfuzz.common.ast.visitors.StandardVisitor;
import com.graphicsfuzz.common.typing.Scope;
-import com.graphicsfuzz.common.typing.ScopeTreeBuilder;
-import com.graphicsfuzz.common.util.IRandom;
import com.graphicsfuzz.common.util.IdGenerator;
-import com.graphicsfuzz.generator.mutateapi.MutationFinder;
import com.graphicsfuzz.generator.mutateapi.MutationFinderBase;
-import com.graphicsfuzz.generator.util.TransformationProbabilities;
import com.graphicsfuzz.util.Constants;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
-import java.util.stream.Collectors;
public class OutlineStatementMutationFinder extends MutationFinderBase {
@@ -74,11 +66,11 @@ public void visitExprStmt(ExprStmt exprStmt) {
if (!OutlineStatementMutation.assignsDirectlyToVariable(be)) {
return;
}
- if (referencesArray(be.getRhs(), currentScope)) {
+ if (referencesArray(be.getRhs(), getCurrentScope())) {
return;
}
- addMutation(new OutlineStatementMutation(exprStmt, currentScope, getTranslationUnit(),
- enclosingFunction, idGenerator));
+ addMutation(new OutlineStatementMutation(exprStmt, getCurrentScope(), getTranslationUnit(),
+ getEnclosingFunction(), idGenerator));
}
private boolean referencesArray(Expr expr, Scope enclosingScope) {
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/SplitForLoopMutation.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/SplitForLoopMutation.java
index ae356524f..e1a9da067 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/SplitForLoopMutation.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/SplitForLoopMutation.java
@@ -16,7 +16,7 @@
package com.graphicsfuzz.generator.semanticspreserving;
-import com.graphicsfuzz.common.ast.decl.ScalarInitializer;
+import com.graphicsfuzz.common.ast.decl.Initializer;
import com.graphicsfuzz.common.ast.decl.VariableDeclInfo;
import com.graphicsfuzz.common.ast.decl.VariablesDeclaration;
import com.graphicsfuzz.common.ast.expr.BinOp;
@@ -102,7 +102,7 @@ private void adjustInitializer(ForStmt loop, int numIterationsToSplitAfter,
+ (loopSplitInfo.getIncreasing() ? 1 : -1) * numIterationsToSplitAfter;
VariablesDeclaration varDecl = ((DeclarationStmt) loop.getInit()).getVariablesDeclaration();
- varDecl.getDeclInfo(0).setInitializer(new ScalarInitializer(new IntConstantExpr(
+ varDecl.getDeclInfo(0).setInitializer(new Initializer(new IntConstantExpr(
String.valueOf(newStart))));
}
@@ -179,12 +179,12 @@ private static Optional maybeGetLoopSplitInfo(ForStmt forStmt) {
}
// Now we grab the initial value, which needs to be an integer.
- if (!(declInfo.getInitializer() instanceof ScalarInitializer)) {
+ if (!(declInfo.getInitializer() instanceof Initializer)) {
return Optional.empty();
}
// Now we get its integer value, if it has one
- final Optional maybeStartValue = maybeGetIntegerValue(((ScalarInitializer) declInfo
+ final Optional maybeStartValue = maybeGetIntegerValue((declInfo
.getInitializer()).getExpr());
if (!maybeStartValue.isPresent()) {
return Optional.empty();
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/StructificationMutation.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/StructificationMutation.java
index 9f1d74fd0..055903e12 100755
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/StructificationMutation.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/StructificationMutation.java
@@ -18,7 +18,7 @@
import com.graphicsfuzz.common.ast.IParentMap;
import com.graphicsfuzz.common.ast.TranslationUnit;
-import com.graphicsfuzz.common.ast.decl.ScalarInitializer;
+import com.graphicsfuzz.common.ast.decl.Initializer;
import com.graphicsfuzz.common.ast.decl.VariableDeclInfo;
import com.graphicsfuzz.common.ast.decl.VariablesDeclaration;
import com.graphicsfuzz.common.ast.expr.Expr;
@@ -33,7 +33,7 @@
import com.graphicsfuzz.common.ast.type.Type;
import com.graphicsfuzz.common.glslversion.ShadingLanguageVersion;
import com.graphicsfuzz.common.typing.ScopeEntry;
-import com.graphicsfuzz.common.typing.ScopeTreeBuilder;
+import com.graphicsfuzz.common.typing.ScopeTrackingVisitor;
import com.graphicsfuzz.common.typing.SupportedTypes;
import com.graphicsfuzz.common.util.IRandom;
import com.graphicsfuzz.common.util.IdGenerator;
@@ -177,9 +177,9 @@ private void structifyDeclaration(String enclosingStructVariableName,
declInfo.setName(enclosingStructVariableName);
if (declInfo.hasInitializer()) {
declInfo.setInitializer(
- new ScalarInitializer(
+ new Initializer(
makeInitializationExpr(enclosingStructType,
- ((ScalarInitializer) declInfo.getInitializer()).getExpr())
+ (declInfo.getInitializer()).getExpr())
)
);
}
@@ -207,11 +207,11 @@ private void structifyBlock(Expr structifiedExpr) {
final IParentMap parentMap = IParentMap.createParentMap(block);
- new ScopeTreeBuilder() {
+ new ScopeTrackingVisitor() {
@Override
public void visitVariableIdentifierExpr(VariableIdentifierExpr variableIdentifierExpr) {
super.visitVariableIdentifierExpr(variableIdentifierExpr);
- ScopeEntry se = currentScope.lookupScopeEntry(variableIdentifierExpr.getName());
+ ScopeEntry se = getCurrentScope().lookupScopeEntry(variableIdentifierExpr.getName());
if (se == null) {
// We are traversing a block in isolation, so we won't have a scope entry for any variable
// declared outside the block.
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/VectorizeMutation.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/VectorizeMutation.java
index 43ef01c6d..fb3db2c74 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/VectorizeMutation.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/VectorizeMutation.java
@@ -28,7 +28,7 @@
import com.graphicsfuzz.common.ast.stmt.ExprStmt;
import com.graphicsfuzz.common.transformreduce.MergeSet;
import com.graphicsfuzz.common.typing.ScopeEntry;
-import com.graphicsfuzz.common.typing.ScopeTreeBuilder;
+import com.graphicsfuzz.common.typing.ScopeTrackingVisitor;
import com.graphicsfuzz.common.util.ListConcat;
import com.graphicsfuzz.generator.mutateapi.Mutation;
import java.util.ArrayList;
@@ -61,7 +61,7 @@ public void apply() {
mergeSet.getMergedType(), new VariableDeclInfo(mergeSet.getMergedName(), null, null))));
}
- private class VectorizerVisitor extends ScopeTreeBuilder {
+ private class VectorizerVisitor extends ScopeTrackingVisitor {
private boolean inDeclarationOfTargetVariable = false;
private final ScopeEntry currentComponent;
@@ -96,19 +96,20 @@ public void visitVariableIdentifierExpr(VariableIdentifierExpr variableIdentifie
}
private boolean isCurrentComponentVariable(String name) {
- ScopeEntry entry = currentScope.lookupScopeEntry(name);
+ ScopeEntry entry = getCurrentScope().lookupScopeEntry(name);
return entry != null && entry.hasVariableDeclInfo()
&& currentComponent.getVariableDeclInfo() == entry.getVariableDeclInfo();
}
@Override
public void visitDeclarationStmt(DeclarationStmt declarationStmt) {
- List existingKeys = new ArrayList<>();
- existingKeys.addAll(currentScope.keys());
+ List existingKeys = new ArrayList<>(getCurrentScope().keys());
super.visitDeclarationStmt(declarationStmt);
- List newKeys = currentScope.keys().stream().filter(key -> !existingKeys.contains(key))
+ List newKeys = getCurrentScope().keys()
+ .stream()
+ .filter(key -> !existingKeys.contains(key))
+ .sorted(String::compareTo)
.collect(Collectors.toList());
- newKeys.sort(String::compareTo);
for (String newKey : newKeys) {
if (isCurrentComponentVariable(newKey)) {
final ExprStmt insertedStmt = new ExprStmt(
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/VectorizeMutationFinder.java b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/VectorizeMutationFinder.java
index 513b1c3e0..b056590c4 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/VectorizeMutationFinder.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/semanticspreserving/VectorizeMutationFinder.java
@@ -26,12 +26,9 @@
import com.graphicsfuzz.common.ast.type.TypeQualifier;
import com.graphicsfuzz.common.transformreduce.MergeSet;
import com.graphicsfuzz.common.typing.ScopeEntry;
-import com.graphicsfuzz.common.typing.ScopeTreeBuilder;
import com.graphicsfuzz.common.util.IRandom;
-import com.graphicsfuzz.generator.mutateapi.MutationFinder;
import com.graphicsfuzz.generator.mutateapi.MutationFinderBase;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -60,8 +57,8 @@ protected void popScope() {
assert lastExitedBlock != null;
if (!(parentMap.getParent(lastExitedBlock) instanceof SwitchStmt)) {
List mergeSetsForThisScope = new ArrayList<>();
- for (String v : currentScope.keys()) {
- ScopeEntry entry = currentScope.lookupScopeEntry(v);
+ for (String v : getCurrentScope().keys()) {
+ ScopeEntry entry = getCurrentScope().lookupScopeEntry(v);
if (!isCandidateForMerging(entry)) {
continue;
}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/tool/CustomMutatorServer.java b/generator/src/main/java/com/graphicsfuzz/generator/tool/CustomMutatorServer.java
index 21b02eee6..a32b71997 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/tool/CustomMutatorServer.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/tool/CustomMutatorServer.java
@@ -130,8 +130,7 @@ private static String mutate(String inputShader, int seed, boolean isFragment) {
Optional.empty(),
stream,
INDENTATION_WIDTH,
- PrettyPrinterVisitor.DEFAULT_NEWLINE_SUPPLIER,
- false);
+ PrettyPrinterVisitor.DEFAULT_NEWLINE_SUPPLIER);
}
final String outputShader =
new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8);
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/tool/Fragment2Compute.java b/generator/src/main/java/com/graphicsfuzz/generator/tool/Fragment2Compute.java
index a3ff3e972..a2b4da8a2 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/tool/Fragment2Compute.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/tool/Fragment2Compute.java
@@ -45,7 +45,7 @@
import com.graphicsfuzz.common.ast.visitors.StandardVisitor;
import com.graphicsfuzz.common.transformreduce.GlslShaderJob;
import com.graphicsfuzz.common.transformreduce.ShaderJob;
-import com.graphicsfuzz.common.typing.ScopeTreeBuilder;
+import com.graphicsfuzz.common.typing.ScopeTrackingVisitor;
import com.graphicsfuzz.common.util.GlslParserException;
import com.graphicsfuzz.common.util.IRandom;
import com.graphicsfuzz.common.util.OpenGlConstants;
@@ -56,6 +56,7 @@
import com.graphicsfuzz.common.util.ShaderKind;
import com.graphicsfuzz.common.util.SsboFieldData;
import com.graphicsfuzz.generator.util.RemoveDiscardStatements;
+import com.graphicsfuzz.util.ArgsUtil;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
@@ -63,7 +64,6 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
-import java.util.Random;
import java.util.stream.Collectors;
import net.sourceforge.argparse4j.ArgumentParsers;
import net.sourceforge.argparse4j.impl.Arguments;
@@ -99,8 +99,8 @@ private static Namespace parse(String[] args) throws ArgumentParserException {
.type(File.class);
parser.addArgument("--seed")
- .help("Seed for random number generator.")
- .type(Integer.class);
+ .help("Seed (unsigned 64 bit long integer) for random number generator.")
+ .type(String.class);
parser.addArgument("--generate-uniform-bindings")
.help("Put all uniforms in uniform blocks and generate bindings; required for Vulkan "
@@ -179,7 +179,7 @@ private static String demoteOutputVariable(TranslationUnit tu) {
* Replace all references to gl_FragCoord with gl_GlobalInvocationID.
*/
private static void replaceFragCoordWithIdLookup(TranslationUnit computeTu) {
- new ScopeTreeBuilder() {
+ new ScopeTrackingVisitor() {
private IParentMap parentMap = IParentMap.createParentMap(computeTu);
@@ -188,7 +188,7 @@ public void visitVariableIdentifierExpr(VariableIdentifierExpr variableIdentifie
super.visitVariableIdentifierExpr(variableIdentifierExpr);
if (variableIdentifierExpr.getName().equals(OpenGlConstants.GL_FRAG_COORD)) {
// It has the right name.
- if (currentScope.lookupScopeEntry(OpenGlConstants.GL_FRAG_COORD) == null) {
+ if (getCurrentScope().lookupScopeEntry(OpenGlConstants.GL_FRAG_COORD) == null) {
// It has no scope; i.e., it is built-in - so it is the real gl_FragCoord.
// Replace it with something made from gl_GlobalInvocationID.
parentMap.getParent(variableIdentifierExpr).replaceChild(variableIdentifierExpr,
@@ -307,8 +307,7 @@ public static void mainHelper(String... args) throws ArgumentParserException,
InterruptedException, GlslParserException, ParseTimeoutException, IOException {
final Namespace ns = parse(args);
final ShaderJobFileOperations fileOps = new ShaderJobFileOperations();
- final IRandom generator = new RandomWrapper(ns.getInt("seed") == null
- ? new Random().nextInt() : ns.getInt("seed"));
+ final IRandom generator = new RandomWrapper(ArgsUtil.getSeedArgument(ns));
final ShaderJob transformedShaderJob = transform(
fileOps.readShaderJobFile(ns.get("fragment_json")), generator);
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/tool/FuzzShader.java b/generator/src/main/java/com/graphicsfuzz/generator/tool/FuzzShader.java
deleted file mode 100644
index 91e21e7b9..000000000
--- a/generator/src/main/java/com/graphicsfuzz/generator/tool/FuzzShader.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2018 The GraphicsFuzz Project Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.graphicsfuzz.generator.tool;
-
-import com.graphicsfuzz.common.glslversion.ShadingLanguageVersion;
-import com.graphicsfuzz.common.tool.PrettyPrinterVisitor;
-import com.graphicsfuzz.common.util.RandomWrapper;
-import com.graphicsfuzz.common.util.ShaderKind;
-import com.graphicsfuzz.generator.fuzzer.Fuzzer;
-import com.graphicsfuzz.generator.fuzzer.FuzzingContext;
-import com.graphicsfuzz.generator.util.GenerationParams;
-
-public class FuzzShader {
-
- public static void main(String[] args) {
-
- if (args.length != 1) {
- System.err.print("Usage: seed");
- System.exit(1);
- }
-
- new PrettyPrinterVisitor(System.out)
- .visit(new Fuzzer(new FuzzingContext(),
- ShadingLanguageVersion.GLSL_440,
- new RandomWrapper(Integer.parseInt(args[0])),
- GenerationParams.normal(ShaderKind.FRAGMENT, true)).fuzzTranslationUnit());
-
- }
-
-}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/tool/Generate.java b/generator/src/main/java/com/graphicsfuzz/generator/tool/Generate.java
index dce9cc76c..5a40c701b 100755
--- a/generator/src/main/java/com/graphicsfuzz/generator/tool/Generate.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/tool/Generate.java
@@ -27,7 +27,6 @@
import com.graphicsfuzz.common.typing.Typer;
import com.graphicsfuzz.common.util.GlslParserException;
import com.graphicsfuzz.common.util.IRandom;
-import com.graphicsfuzz.common.util.IdGenerator;
import com.graphicsfuzz.common.util.ParseTimeoutException;
import com.graphicsfuzz.common.util.PipelineInfo;
import com.graphicsfuzz.common.util.PruneUniforms;
@@ -54,6 +53,7 @@
import com.graphicsfuzz.generator.util.FloatLiteralReplacer;
import com.graphicsfuzz.generator.util.GenerationParams;
import com.graphicsfuzz.generator.util.TransformationProbabilities;
+import com.graphicsfuzz.util.ArgsUtil;
import com.graphicsfuzz.util.Constants;
import java.io.File;
import java.io.IOException;
@@ -61,7 +61,6 @@
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
-import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import net.sourceforge.argparse4j.ArgumentParsers;
@@ -90,12 +89,8 @@ private static Namespace parse(String[] args) throws ArgumentParserException {
.help("Path of folder of donor shaders.")
.type(File.class);
- parser.addArgument("glsl-version")
- .help("Version of GLSL to target.")
- .type(String.class);
-
parser.addArgument("output")
- .help("Output shader job file file (.json.")
+ .help("Output shader job file file (.json).")
.type(File.class);
addGeneratorCommonArguments(parser);
@@ -106,8 +101,8 @@ private static Namespace parse(String[] args) throws ArgumentParserException {
public static void addGeneratorCommonArguments(ArgumentParser parser) {
parser.addArgument("--seed")
- .help("Seed to initialize random number generator with.")
- .type(Integer.class);
+ .help("Seed (unsigned 64 bit long integer) for the random number generator.")
+ .type(String.class);
parser.addArgument("--small")
.help("Try to generate small shaders.")
@@ -169,9 +164,8 @@ public static void addGeneratorCommonArguments(ArgumentParser parser) {
*/
public static StringBuilder generateVariant(ShaderJob shaderJob,
GeneratorArguments args,
- int seed) {
+ IRandom random) {
final StringBuilder result = new StringBuilder();
- final IRandom random = new RandomWrapper(seed);
if (args.getAddInjectionSwitch()) {
for (TranslationUnit shader : shaderJob.getShaders()) {
@@ -211,7 +205,7 @@ public static StringBuilder generateVariant(ShaderJob shaderJob,
* @param referenceShaderJobFile The shader job to be transformed.
* @param outputShaderJobFile Output file for the variant.
* @param generatorArguments Arguments to control generation.
- * @param seed Seed for random number generation.
+ * @param random Random number generator.
* @param writeProbabilities Records whether details about probabilities should be written.
* @throws IOException if file reading or writing goes wrong.
* @throws ParseTimeoutException if parsing takes too long.
@@ -219,11 +213,12 @@ public static StringBuilder generateVariant(ShaderJob shaderJob,
* preprocessor or validator.
* @throws GlslParserException if a shader in the job fails to parse.
*/
+ @SuppressWarnings("deprecation")
public static void generateVariant(ShaderJobFileOperations fileOps,
File referenceShaderJobFile,
File outputShaderJobFile,
GeneratorArguments generatorArguments,
- int seed,
+ IRandom random,
boolean writeProbabilities)
throws IOException, ParseTimeoutException, InterruptedException, GlslParserException {
// This is mutated into the variant.
@@ -232,7 +227,7 @@ public static void generateVariant(ShaderJobFileOperations fileOps,
final StringBuilder generationInfo = generateVariant(
variantShaderJob,
generatorArguments,
- seed);
+ random);
fileOps.writeShaderJobFile(
variantShaderJob,
@@ -253,7 +248,7 @@ private static StringBuilder transformShader(TranslationUnit shaderToTransform,
GeneratorArguments args) {
final ShaderKind shaderKind = shaderToTransform.getShaderKind();
StringBuilder result = new StringBuilder();
- result.append("======\n" + shaderKind + ":\n");
+ result.append("======\n").append(shaderKind).append(":\n");
if (args.getReplaceFloatLiterals()) {
FloatLiteralReplacer.replace(
@@ -310,6 +305,9 @@ private static StringBuilder transformShader(TranslationUnit shaderToTransform,
public static void main(String[] args) {
try {
mainHelper(args);
+ } catch (ArgumentParserException exception) {
+ exception.getParser().handleError(exception);
+ System.exit(1);
} catch (Throwable exception) {
LOGGER.error("", exception);
System.exit(1);
@@ -321,17 +319,14 @@ public static void mainHelper(String[] args)
GlslParserException {
final Namespace ns = parse(args);
- Integer seed = ns.get("seed");
- if (seed == null) {
- seed = new Random().nextInt();
- }
+ final IRandom random = new RandomWrapper(ArgsUtil.getSeedArgument(ns));
generateVariant(
new ShaderJobFileOperations(),
ns.get("reference_json"),
ns.get("output"),
getGeneratorArguments(ns),
- seed,
+ random,
ns.getBoolean("write_probabilities"));
}
@@ -418,20 +413,20 @@ private static String applyTransformationsMultiPass(GeneratorArguments args,
}
List nextRoundTransformations = new ArrayList<>();
- String result = "";
+ final StringBuilder result = new StringBuilder();
// Keep applying transformations until all transformations cease to be effective, or
// we get a large enough shader.
while (!transformations.isEmpty() && !shaderLargeEnough(reference, generator)) {
ITransformation transformation = transformations.remove(generator.nextInt(
transformations.size()));
- result += transformation.getName() + "\n";
+ result.append(transformation.getName()).append("\n");
if (transformation.apply(reference, probabilities,
generator.spawnChild(),
generationParams)) {
// Keep the size down by stripping unused stuff.
StripUnusedFunctions.strip(reference);
StripUnusedGlobals.strip(reference);
- assert canTypeCheckWithoutFailure(reference, reference.getShadingLanguageVersion());
+ assert canTypeCheckWithoutFailure(reference);
// Only if the transformation applied successfully (i.e., made a change), do we add it
// to the list of transformations to be applied next round.
@@ -442,14 +437,13 @@ private static String applyTransformationsMultiPass(GeneratorArguments args,
nextRoundTransformations = new ArrayList<>();
}
}
- return result;
+ return result.toString();
}
- private static boolean canTypeCheckWithoutFailure(TranslationUnit reference,
- ShadingLanguageVersion shadingLanguageVersion) {
+ private static boolean canTypeCheckWithoutFailure(TranslationUnit reference) {
// Debugging aid: fail early if we end up messing up the translation unit so that type checking
// does not work.
- new Typer(reference, shadingLanguageVersion);
+ new Typer(reference);
return true;
}
@@ -591,7 +585,7 @@ private static String applyControlFlowComplication(GeneratorArguments args,
public static void randomiseUnsetUniforms(TranslationUnit tu, PipelineInfo pipelineInfo,
IRandom generator) {
- final Supplier floatSupplier = () -> generator.nextFloat();
+ final Supplier floatSupplier = generator::nextFloat;
final Supplier intSupplier = () -> generator.nextInt(1 << 15);
final Supplier uintSupplier = () -> generator.nextInt(1 << 15);
final Supplier boolSupplier = () -> generator.nextInt(2);
@@ -610,14 +604,14 @@ public static void addInjectionSwitchIfNotPresent(TranslationUnit tu) {
private static boolean alreadyDeclaresInjectionSwitch(TranslationUnit tu) {
return tu.getGlobalVarDeclInfos()
.stream()
- .map(item -> item.getName())
+ .map(VariableDeclInfo::getName)
.collect(Collectors.toList())
.contains(Constants.INJECTION_SWITCH);
}
public static void setInjectionSwitch(PipelineInfo pipelineInfo) {
pipelineInfo.addUniform(Constants.INJECTION_SWITCH, BasicType.VEC2, Optional.empty(),
- Arrays.asList(new Float(0.0), new Float(1.0)));
+ Arrays.asList(0.0f, 1.0f));
}
}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/tool/GenerateShaderFamily.java b/generator/src/main/java/com/graphicsfuzz/generator/tool/GenerateShaderFamily.java
index 68f70f514..5900c31b4 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/tool/GenerateShaderFamily.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/tool/GenerateShaderFamily.java
@@ -128,7 +128,7 @@ public static void mainHelper(String[] args) throws ArgumentParserException,
final boolean writeProbabilities = ns.getBoolean("write_probabilities");
final boolean keepBadVariants = ns.getBoolean("keep_bad_variants");
final boolean stopOnFail = ns.getBoolean("stop_on_fail");
- final int seed = ArgsUtil.getSeedArgument(ns);
+ final IRandom generator = new RandomWrapper(ArgsUtil.getSeedArgument(ns));
final int numVariants = ns.getInt("num_variants");
Optional maxBytes = ns.get("max_bytes") == null ? Optional.empty() :
Optional.of(ns.getInt("max_bytes"));
@@ -137,7 +137,7 @@ public static void mainHelper(String[] args) throws ArgumentParserException,
final GeneratorArguments generatorArguments = Generate.getGeneratorArguments(ns);
if (verbose) {
- LOGGER.info("Using seed " + seed);
+ LOGGER.info("Using random: " + generator.getDescription());
}
if (!referenceShaderJob.isFile()) {
@@ -207,8 +207,6 @@ public static void mainHelper(String[] args) throws ArgumentParserException,
int generatedVariants = 0;
int triedVariants = 0;
- final IRandom generator = new RandomWrapper(seed);
-
// Main variant generation loop
while (generatedVariants < numVariants) {
if (verbose) {
@@ -216,29 +214,28 @@ public static void mainHelper(String[] args) throws ArgumentParserException,
+ " of " + numVariants + ")");
}
// Generate a variant
- final int innerSeed = generator.nextInt(Integer.MAX_VALUE);
- if (verbose) {
- LOGGER.info("Generating variant with inner seed " + innerSeed);
- }
final File variantShaderJobFile = new File(outputDir, "variant_" + String.format("%03d",
generatedVariants) + ".json");
+ final IRandom childRandom = generator.spawnChild();
+ if (verbose) {
+ LOGGER.info("Generating variant with inner random: " + childRandom.getDescription());
+ }
triedVariants++;
try {
Generate.generateVariant(fileOps, referenceShaderJob, variantShaderJobFile,
- generatorArguments, innerSeed, writeProbabilities);
+ generatorArguments, childRandom, writeProbabilities);
} catch (Exception exception) {
- if (verbose) {
- LOGGER.error("Failed generating variant: " + exception.getMessage()
- + exception.getStackTrace()
+ /*if (verbose)*/ {
+ LOGGER.error("Failed generating variant: "
+ "\nGenerator arguments: " + generatorArguments
+ "\nReference shader job: " + referenceShaderJob
- + "\nSeed: " + innerSeed);
+ + "\nRandom: " + childRandom.getDescription(), exception);
}
if (stopOnFail) {
final String message = "Failed generating a variant, stopping.";
LOGGER.info(message);
- throw new RuntimeException(message);
+ throw new RuntimeException(message, exception);
}
continue;
}
@@ -283,7 +280,7 @@ public static void mainHelper(String[] args) throws ArgumentParserException,
StandardCharsets.UTF_8) : "none";
infoLog.addProperty("git_hash", hashContents);
infoLog.addProperty("args", String.join(" ", args));
- infoLog.addProperty("seed", seed);
+ infoLog.addProperty("seed", generator.getDescription());
// Pretty-print the info log.
FileUtils.writeStringToFile(new File(outputDir,"infolog.json"),
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/tool/GlslGenerate.java b/generator/src/main/java/com/graphicsfuzz/generator/tool/GlslGenerate.java
index d7028ac21..46d602414 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/tool/GlslGenerate.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/tool/GlslGenerate.java
@@ -83,7 +83,7 @@ public static void mainHelper(String[] args, boolean failOnReferencePreparationE
final String prefix = ns.get("prefix");
final int numVariants = ns.getInt("num_variants");
final boolean verbose = ns.getBoolean("verbose");
- final int seed = ArgsUtil.getSeedArgument(ns);
+ final IRandom generator = new RandomWrapper(ArgsUtil.getSeedArgument(ns));
final ShaderJobFileOperations fileOps = new ShaderJobFileOperations();
@@ -100,7 +100,6 @@ public static void mainHelper(String[] args, boolean failOnReferencePreparationE
+ " variant" + (numVariants == 1 ? "" : "s") + ".");
}
- final IRandom generator = new RandomWrapper(seed);
int referenceCount = 0;
for (File shaderJobFile : referenceShaderJobFiles) {
LOGGER.info("Generating family " + referenceCount + " from reference "
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/tool/KnownValueShaderGenerator.java b/generator/src/main/java/com/graphicsfuzz/generator/tool/KnownValueShaderGenerator.java
new file mode 100644
index 000000000..e6cf78c05
--- /dev/null
+++ b/generator/src/main/java/com/graphicsfuzz/generator/tool/KnownValueShaderGenerator.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2019 The GraphicsFuzz Project Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphicsfuzz.generator.tool;
+
+import com.graphicsfuzz.common.ast.TranslationUnit;
+import com.graphicsfuzz.common.ast.decl.FunctionDefinition;
+import com.graphicsfuzz.common.ast.decl.FunctionPrototype;
+import com.graphicsfuzz.common.ast.decl.PrecisionDeclaration;
+import com.graphicsfuzz.common.ast.decl.VariableDeclInfo;
+import com.graphicsfuzz.common.ast.decl.VariablesDeclaration;
+import com.graphicsfuzz.common.ast.expr.BinOp;
+import com.graphicsfuzz.common.ast.expr.BinaryExpr;
+import com.graphicsfuzz.common.ast.expr.Expr;
+import com.graphicsfuzz.common.ast.expr.TypeConstructorExpr;
+import com.graphicsfuzz.common.ast.expr.VariableIdentifierExpr;
+import com.graphicsfuzz.common.ast.stmt.BlockStmt;
+import com.graphicsfuzz.common.ast.stmt.ExprStmt;
+import com.graphicsfuzz.common.ast.stmt.NullStmt;
+import com.graphicsfuzz.common.ast.stmt.Stmt;
+import com.graphicsfuzz.common.ast.type.BasicType;
+import com.graphicsfuzz.common.ast.type.LayoutQualifierSequence;
+import com.graphicsfuzz.common.ast.type.LocationLayoutQualifier;
+import com.graphicsfuzz.common.ast.type.QualifiedType;
+import com.graphicsfuzz.common.ast.type.TypeQualifier;
+import com.graphicsfuzz.common.ast.type.VoidType;
+import com.graphicsfuzz.common.glslversion.ShadingLanguageVersion;
+import com.graphicsfuzz.common.transformreduce.GlslShaderJob;
+import com.graphicsfuzz.common.transformreduce.ShaderJob;
+import com.graphicsfuzz.common.util.IRandom;
+import com.graphicsfuzz.common.util.PipelineInfo;
+import com.graphicsfuzz.common.util.PruneUniforms;
+import com.graphicsfuzz.common.util.RandomWrapper;
+import com.graphicsfuzz.common.util.ShaderJobFileOperations;
+import com.graphicsfuzz.generator.knownvaluegeneration.ExpressionGenerator;
+import com.graphicsfuzz.generator.knownvaluegeneration.FactManager;
+import com.graphicsfuzz.generator.knownvaluegeneration.NumericValue;
+import com.graphicsfuzz.util.ArgsUtil;
+import com.graphicsfuzz.util.Constants;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.stream.Stream;
+import net.sourceforge.argparse4j.ArgumentParsers;
+import net.sourceforge.argparse4j.impl.Arguments;
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+import net.sourceforge.argparse4j.inf.ArgumentParserException;
+import net.sourceforge.argparse4j.inf.Namespace;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class KnownValueShaderGenerator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(KnownValueShaderGenerator.class);
+
+ private static Namespace parse(String[] args) throws ArgumentParserException {
+ ArgumentParser parser = ArgumentParsers.newArgumentParser("KnownValueShaderGenerator")
+ .defaultHelp(true)
+ .description("Generate a known value shader with the desired RGBA color.");
+
+ parser.addArgument("output")
+ .help("Output shader job file file (.json).")
+ .type(File.class);
+
+ parser.addArgument("r")
+ .help("Floating point number of red value.")
+ .type(Float.class);
+
+ parser.addArgument("g")
+ .help("Floating point number of green value.")
+ .type(Float.class);
+
+ parser.addArgument("b")
+ .help("Floating point number of blue value.")
+ .type(Float.class);
+
+ parser.addArgument("a")
+ .help("Floating point number of alpha value.")
+ .type(Float.class);
+
+ parser.addArgument("--version")
+ .help("Shading language version of a generated fragment shader.")
+ .setDefault("410")
+ .type(String.class);
+
+ parser.addArgument("--seed")
+ .help("Seed (unsigned 64 bit long integer) for the random number generator.")
+ .type(String.class);
+
+ parser.addArgument("--max-uniforms")
+ .help("Ensure that generated shaders have no more than the given number of uniforms; "
+ + "required for Vulkan compatibility.")
+ .setDefault(0)
+ .type(Integer.class);
+
+ parser.addArgument("--generate-uniform-bindings")
+ .help("Put all uniforms in uniform blocks and generate bindings; required for Vulkan "
+ + "compatibility.")
+ .action(Arguments.storeTrue());
+
+ return parser.parseArgs(args);
+ }
+
+ public static void mainHelper(String[] args) throws ArgumentParserException, IOException {
+ final Namespace ns = parse(args);
+ final float rFloat = ns.getFloat("r");
+ final float gFloat = ns.getFloat("g");
+ final float bFloat = ns.getFloat("b");
+ final float aFloat = ns.getFloat("a");
+ final int maxUniforms = ns.getInt("max_uniforms");
+ final boolean generateUniformBindings = ns.getBoolean("generate_uniform_bindings");
+ final IRandom generator = new RandomWrapper(ArgsUtil.getSeedArgument(ns));
+ final String version = ns.getString("version");
+ final File shaderJobFile = ns.get("output");
+ final ShaderJobFileOperations fileOps = new ShaderJobFileOperations();
+
+ final TranslationUnit tu =
+ new TranslationUnit(Optional.of(ShadingLanguageVersion.fromVersionString(version)),
+ Arrays.asList(
+ new PrecisionDeclaration("precision highp float;"),
+ new PrecisionDeclaration("precision highp int;"),
+ new VariablesDeclaration(new QualifiedType(BasicType.VEC4,
+ Arrays.asList(new LayoutQualifierSequence(
+ new LocationLayoutQualifier(0)), TypeQualifier.SHADER_OUTPUT)),
+ new VariableDeclInfo("_GLF_color", null, null)),
+ new FunctionDefinition(
+ new FunctionPrototype("main", VoidType.VOID, Collections.emptyList()),
+ new BlockStmt(new ArrayList<>(), false))));
+
+ final PipelineInfo pipelineInfo = new PipelineInfo();
+ final FactManager globalFactManager = new FactManager(null);
+
+ // A placeholder statement for what will eventually be the color assignment.
+ final Stmt placeholderForColorAssignment = new NullStmt();
+ tu.getMainFunction().getBody().addStmt(placeholderForColorAssignment);
+
+ LOGGER.info("About to generate the known value fragment shader"
+ + " with the parameters R = " + rFloat + ", G = " + gFloat + ", B = " + bFloat + " and"
+ + " A = " + aFloat + ".");
+
+ final ExpressionGenerator expressionGenerator = new
+ ExpressionGenerator(tu, pipelineInfo, generator, globalFactManager);
+ final FactManager mainFactManager = globalFactManager.newScope();
+ final Expr[] rgbaExprs = Stream.of(rFloat, gFloat, bFloat, aFloat)
+ .map(item -> expressionGenerator.generateExpr(
+ mainFactManager,
+ tu.getMainFunction(),
+ placeholderForColorAssignment,
+ new NumericValue(BasicType.FLOAT, Optional.of(item)))).toArray(Expr[]::new);
+
+ tu.getMainFunction().getBody().replaceChild(placeholderForColorAssignment,
+ new ExprStmt(new BinaryExpr(new VariableIdentifierExpr("_GLF_color"),
+ new TypeConstructorExpr("vec4", rgbaExprs), BinOp.ASSIGN)));
+
+ final ShaderJob shaderJob = new GlslShaderJob(Optional.empty(), pipelineInfo, tu);
+
+ if (maxUniforms > 0) {
+ PruneUniforms.prune(shaderJob, maxUniforms, Arrays.asList(Constants.GLF_UNIFORM));
+ }
+ if (generateUniformBindings) {
+ shaderJob.makeUniformBindings();
+ }
+
+ fileOps.writeShaderJobFile(shaderJob, shaderJobFile);
+ LOGGER.info("Generation complete.");
+ }
+
+ public static void main(String[] args) {
+ try {
+ mainHelper(args);
+ } catch (ArgumentParserException exception) {
+ exception.getParser().handleError(exception);
+ System.exit(1);
+ } catch (IOException exception) {
+ exception.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/tool/Mutate.java b/generator/src/main/java/com/graphicsfuzz/generator/tool/Mutate.java
index f839f09c1..31ff13355 100644
--- a/generator/src/main/java/com/graphicsfuzz/generator/tool/Mutate.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/tool/Mutate.java
@@ -18,12 +18,15 @@
import com.graphicsfuzz.common.ast.TranslationUnit;
import com.graphicsfuzz.common.tool.PrettyPrinterVisitor;
+import com.graphicsfuzz.common.transformreduce.GlslShaderJob;
import com.graphicsfuzz.common.util.GlslParserException;
import com.graphicsfuzz.common.util.IRandom;
import com.graphicsfuzz.common.util.IdGenerator;
import com.graphicsfuzz.common.util.ParseHelper;
import com.graphicsfuzz.common.util.ParseTimeoutException;
+import com.graphicsfuzz.common.util.PipelineInfo;
import com.graphicsfuzz.common.util.RandomWrapper;
+import com.graphicsfuzz.common.util.ShaderJobFileOperations;
import com.graphicsfuzz.generator.mutateapi.Mutation;
import com.graphicsfuzz.generator.mutateapi.MutationFinder;
import com.graphicsfuzz.generator.semanticschanging.AddArrayMutationFinder;
@@ -62,6 +65,7 @@
import net.sourceforge.argparse4j.inf.ArgumentParser;
import net.sourceforge.argparse4j.inf.ArgumentParserException;
import net.sourceforge.argparse4j.inf.Namespace;
+import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -84,8 +88,8 @@ private static Namespace parse(String[] args) throws ArgumentParserException {
.type(File.class);
parser.addArgument("--seed")
- .help("Seed to initialize random number generator with.")
- .type(Integer.class);
+ .help("Seed (unsigned 64 bit long integer) for the random number generator.")
+ .type(String.class);
return parser.parseArgs(args);
}
@@ -123,24 +127,21 @@ public static void mainHelper(String[] args) throws ArgumentParserException, IOE
final File input = ns.get("input");
final File output = ns.get("output");
- final int seed = ArgsUtil.getSeedArgument(ns);
+ IRandom random = new RandomWrapper(ArgsUtil.getSeedArgument(ns));
final TranslationUnit tu = ParseHelper.parse(input);
- LOGGER.info("Mutating from " + input + " to " + output + " with seed " + seed);
+ LOGGER.info("Mutating from " + input + " to " + output + " with RNG "
+ + random.getDescription());
- mutate(tu, new RandomWrapper(seed));
+ mutate(tu, random);
- try (PrintStream stream = new PrintStream(new FileOutputStream(output))) {
- PrettyPrinterVisitor.emitShader(
- tu,
- Optional.empty(),
- stream,
- PrettyPrinterVisitor.DEFAULT_INDENTATION_WIDTH,
- PrettyPrinterVisitor.DEFAULT_NEWLINE_SUPPLIER,
- false
- );
- }
+ final File shaderJobFile = new File(FilenameUtils.removeExtension(output.getName()) + ".json");
+
+ final ShaderJobFileOperations fileOps = new ShaderJobFileOperations();
+
+ fileOps.writeShaderJobFile(new GlslShaderJob(Optional.empty(), new PipelineInfo("{}"),
+ tu), shaderJobFile);
}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/tool/PrepareReference.java b/generator/src/main/java/com/graphicsfuzz/generator/tool/PrepareReference.java
index edf877f9b..8def96ba2 100755
--- a/generator/src/main/java/com/graphicsfuzz/generator/tool/PrepareReference.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/tool/PrepareReference.java
@@ -18,6 +18,7 @@
import com.graphicsfuzz.common.ast.TranslationUnit;
import com.graphicsfuzz.common.transformreduce.ShaderJob;
+import com.graphicsfuzz.common.util.AddBraces;
import com.graphicsfuzz.common.util.GlslParserException;
import com.graphicsfuzz.common.util.ParseTimeoutException;
import com.graphicsfuzz.common.util.PipelineInfo;
@@ -150,6 +151,11 @@ private static void prepareReferenceShader(TranslationUnit tu,
if (replaceFloatLiterals) {
FloatLiteralReplacer.replace(tu, pipelineInfo);
}
+
+ // Ensure that all if-then-else statements have braces. This makes the reference easier to
+ // compare with a reduced variant.
+ AddBraces.transform(tu);
+
}
}
diff --git a/generator/src/main/java/com/graphicsfuzz/generator/transformation/DonateCodeTransformation.java b/generator/src/main/java/com/graphicsfuzz/generator/transformation/DonateCodeTransformation.java
index f25a18bca..0979e00dc 100755
--- a/generator/src/main/java/com/graphicsfuzz/generator/transformation/DonateCodeTransformation.java
+++ b/generator/src/main/java/com/graphicsfuzz/generator/transformation/DonateCodeTransformation.java
@@ -22,9 +22,9 @@
import com.graphicsfuzz.common.ast.decl.Declaration;
import com.graphicsfuzz.common.ast.decl.FunctionDefinition;
import com.graphicsfuzz.common.ast.decl.FunctionPrototype;
+import com.graphicsfuzz.common.ast.decl.Initializer;
import com.graphicsfuzz.common.ast.decl.ParameterDecl;
import com.graphicsfuzz.common.ast.decl.PrecisionDeclaration;
-import com.graphicsfuzz.common.ast.decl.ScalarInitializer;
import com.graphicsfuzz.common.ast.decl.VariableDeclInfo;
import com.graphicsfuzz.common.ast.decl.VariablesDeclaration;
import com.graphicsfuzz.common.ast.expr.FunctionCallExpr;
@@ -33,7 +33,6 @@
import com.graphicsfuzz.common.ast.stmt.Stmt;
import com.graphicsfuzz.common.ast.type.ArrayType;
import com.graphicsfuzz.common.ast.type.BasicType;
-import com.graphicsfuzz.common.ast.type.LayoutQualifier;
import com.graphicsfuzz.common.ast.type.LayoutQualifierSequence;
import com.graphicsfuzz.common.ast.type.QualifiedType;
import com.graphicsfuzz.common.ast.type.StructDefinitionType;
@@ -43,7 +42,7 @@
import com.graphicsfuzz.common.ast.visitors.StandardVisitor;
import com.graphicsfuzz.common.glslversion.ShadingLanguageVersion;
import com.graphicsfuzz.common.typing.Scope;
-import com.graphicsfuzz.common.typing.ScopeTreeBuilder;
+import com.graphicsfuzz.common.typing.ScopeTrackingVisitor;
import com.graphicsfuzz.common.typing.Typer;
import com.graphicsfuzz.common.util.GlslParserException;
import com.graphicsfuzz.common.util.IRandom;
@@ -68,24 +67,35 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
public abstract class DonateCodeTransformation implements ITransformation {
- final Function probabilityOfDonation;
+ private final Function probabilityOfDonation;
+ // During a single donation pass, this is populated on demand with the donors that are used.
private Map donorsToTranslationUnits;
+
private final List functionPrototypes;
private final Map globalVariables;
private final Set structNames;
- private final List donorFiles;
+
+ // 'donorFiles' contains those donors that have not yet been used for a code donation. Once a
+ // donor has been used, it is moved to 'usedDonorFiles' (unless it is found to be incompatible,
+ // in which case it is discarded). If 'donorFiles' becomes empty, it and 'usedDonorFiles' are
+ // swapped, so that previously used donors can be used again.
+ private List donorFiles;
+ private List usedDonorFiles;
+
final GenerationParams generationParams;
private int translationUnitCount;
@@ -96,13 +106,14 @@ public DonateCodeTransformation(Function probabilityOfDonation
this.functionPrototypes = new ArrayList<>();
this.globalVariables = new HashMap<>();
this.structNames = new HashSet<>();
+ assert donorsDirectory.exists();
this.donorFiles = new ArrayList<>();
+ this.donorFiles.addAll(Arrays.asList(donorsDirectory.listFiles(
+ pathname -> pathname.getName().endsWith(".frag"))));
+ this.donorFiles.sort(Comparator.naturalOrder());
+ this.usedDonorFiles = new ArrayList<>();
this.generationParams = generationParams;
this.translationUnitCount = 0;
- assert donorsDirectory.exists();
- donorFiles.addAll(Arrays.asList(donorsDirectory.listFiles(
- pathname -> pathname.getName().endsWith(".frag"))));
- donorFiles.sort(Comparator.naturalOrder());
}
/**
@@ -116,20 +127,19 @@ public DonateCodeTransformation(Function probabilityOfDonation
private TranslationUnit prepareTranslationUnit(File donorFile, IRandom generator)
throws IOException, ParseTimeoutException, InterruptedException, GlslParserException {
- TranslationUnit tu = ParseHelper.parse(donorFile);
+ final TranslationUnit tu = ParseHelper.parse(donorFile);
+ final Typer typer = new Typer(tu);
+ // To avoid undefined behaviours, make all array access in bounds for every donor.
+ MakeArrayAccessesInBounds.makeInBounds(tu, typer, tu);
addPrefixes(tu, getDeclaredFunctionNames(tu));
// Add prefixed versions of these builtins, in case they are used.
// Use explicit precision qualifier to avoid introducing errors if there are no float precision
// qualifiers.
- List coordQualifiers = new ArrayList<>();
- coordQualifiers.add(TypeQualifier.MEDIUMP);
tu.addDeclaration(new VariablesDeclaration(
- new QualifiedType(BasicType.VEC4, coordQualifiers),
+ new QualifiedType(BasicType.VEC4, Collections.singletonList(TypeQualifier.MEDIUMP)),
new VariableDeclInfo(addPrefix(OpenGlConstants.GL_FRAG_COORD), null, null)));
- List colorQualifiers = new ArrayList<>();
- colorQualifiers.add(TypeQualifier.MEDIUMP);
tu.addDeclaration(new VariablesDeclaration(
- new QualifiedType(BasicType.VEC4, colorQualifiers),
+ new QualifiedType(BasicType.VEC4, Collections.singletonList(TypeQualifier.MEDIUMP)),
new VariableDeclInfo(addPrefix(OpenGlConstants.GL_FRAG_COLOR), null, null)));
adaptTranslationUnitForSpecificDonation(tu, generator);
translationUnitCount++;
@@ -209,11 +219,19 @@ private Stmt prepareStatementToDonate(IInjectionPoint injectionPoint,
final int maxTries = 10;
int tries = 0;
while (true) {
- DonationContext donationContext = new DonationContextFinder(chooseDonor(generator), generator)
+ final Optional maybeDonor = chooseDonor(generator);
+ if (!maybeDonor.isPresent()) {
+ // No compatible donors were found, thus we cannot do serious code donation here;
+ // we return a null statement instead.
+ return new NullStmt();
+ }
+ DonationContext donationContext = new DonationContextFinder(maybeDonor.get(), generator)
.getDonationContext();
if (incompatible(injectionPoint, donationContext, shadingLanguageVersion)) {
tries++;
if (tries == maxTries) {
+ // We have tried and tried to find something compatible to inject but not managed;
+ // return a null statement instead of a real piece of code to inject.
return new NullStmt();
}
} else {
@@ -251,28 +269,25 @@ public boolean apply(TranslationUnit tu,
}
donateFunctionsAndGlobals(tu);
eliminateUsedDonors();
- makeInjectedArrayAccessesInBounds(tu, injectedStmts, tu.getShadingLanguageVersion());
-
return !injectionPoints.isEmpty();
}
private void eliminateUsedDonors() {
- // Having done donation using a particular donor, we remove it so that we do not use it
- // again in a future pass.
+ // Having done donation using a particular donor, we move it to the list of used donors so that
+ // we only use it again in a future pass once all other donors have been tried.
for (File donor : donorsToTranslationUnits.keySet()) {
+ assert donorFiles.contains(donor);
donorFiles.remove(donor);
+ usedDonorFiles.add(donor);
}
- donorsToTranslationUnits = new HashMap<>();
- }
-
- private void makeInjectedArrayAccessesInBounds(TranslationUnit tu,
- List injectedStmts,
- ShadingLanguageVersion shadingLanguageVersion) {
- Typer typer = new Typer(tu, shadingLanguageVersion);
- for (Stmt stmt : injectedStmts) {
- MakeArrayAccessesInBounds.makeInBounds(stmt, typer);
+ // If there are no donors left -- i.e., if all have been used, then recycle the list of used
+ // donors.
+ if (donorFiles.isEmpty()) {
+ donorFiles = usedDonorFiles;
+ usedDonorFiles = new ArrayList<>();
}
+ donorsToTranslationUnits = new HashMap<>();
}
private boolean incompatible(IInjectionPoint injectionPoint, DonationContext donationContext,
@@ -293,24 +308,24 @@ private boolean incompatible(IInjectionPoint injectionPoint, DonationContext don
}
private Map getGlobalVariablesFromShader(TranslationUnit shader) {
- return new ScopeTreeBuilder() {
+ return new ScopeTrackingVisitor() {
Map getGlobalsFromShader(TranslationUnit shader) {
visit(shader);
Map result = new HashMap<>();
- for (String globalName : currentScope.keys()) {
- result.put(globalName, currentScope.lookupType(globalName));
+ for (String globalName : getCurrentScope().keys()) {
+ result.put(globalName, getCurrentScope().lookupType(globalName));
}
return result;
}
}.getGlobalsFromShader(shader);
}
- final ScalarInitializer getScalarInitializer(IInjectionPoint injectionPoint,
- DonationContext donationContext,
- Type type,
- boolean restrictToConst,
- IRandom generator,
- ShadingLanguageVersion shadingLanguageVersion) {
+ final Initializer getInitializer(IInjectionPoint injectionPoint,
+ DonationContext donationContext,
+ Type type,
+ boolean restrictToConst,
+ IRandom generator,
+ ShadingLanguageVersion shadingLanguageVersion) {
final boolean isConst = type.hasQualifier(TypeQualifier.CONST);
try {
@@ -323,7 +338,7 @@ final ScalarInitializer getScalarInitializer(IInjectionPoint injectionPoint,
scopeForFuzzing.addStructDefinition(sdt);
}
- return new ScalarInitializer(
+ return new Initializer(
new OpaqueExpressionGenerator(generator, generationParams, shadingLanguageVersion)
.fuzzedConstructor(
new Fuzzer(new FuzzingContext(scopeForFuzzing),
@@ -413,14 +428,8 @@ private void donateFunctionsAndGlobals(TranslationUnit recipient) {
newRecipientTopLevelDeclarations.addAll(recipient.getTopLevelDeclarations().stream()
.filter(d -> d instanceof FunctionDefinition).collect(Collectors.toList()));
- // Make sure we clone the top-level FunctionPrototypes that are added, otherwise each will
- // exist twice in the AST: first as a top-level declaration, second as part of the
- // FunctionDefinition.
newRecipientTopLevelDeclarations =
- addNecessaryForwardDeclarations(newRecipientTopLevelDeclarations)
- .stream()
- .map(Declaration::clone)
- .collect(Collectors.toList());
+ addNecessaryForwardDeclarations(newRecipientTopLevelDeclarations);
recipient.setTopLevelDeclarations(newRecipientTopLevelDeclarations);
@@ -480,12 +489,16 @@ private List addNecessaryForwardDeclarations(List decl
Set calledFunctionNames =
getCalledFunctions(decls.get(i));
- Set toDeclare =
+ List toDeclare =
functionsDefinedAfterDecl.get(i).stream().filter(item ->
calledFunctionNames.contains(item.getName())).filter(item ->
!declared.stream().anyMatch(alreadyDeclared -> alreadyDeclared.matches(item)))
- .collect(Collectors.toSet());
- result.addAll(toDeclare);
+ .collect(Collectors.toList());
+
+ // Make sure we clone the FunctionPrototypes that are added, otherwise each will exist
+ // twice in the AST: first as a top-level declaration, second as part of the
+ // FunctionDefinition.
+ result.addAll(toDeclare.stream().map(Declaration::clone).collect(Collectors.toList()));
declared.addAll(toDeclare);
}
if (decls.get(i) instanceof FunctionDefinition) {
@@ -584,25 +597,49 @@ private boolean prototypeMatches(FunctionPrototype fp, List f
return !fs.stream().filter(item -> fp.matches(item)).collect(Collectors.toList()).isEmpty();
}
- TranslationUnit chooseDonor(IRandom generator) {
+ Optional