diff --git a/ast/src/main/java/com/graphicsfuzz/common/glslversion/CompositeShadingLanguageVersion.java b/ast/src/main/java/com/graphicsfuzz/common/glslversion/CompositeShadingLanguageVersion.java index 7c873c6c1..ae2fc2e03 100644 --- a/ast/src/main/java/com/graphicsfuzz/common/glslversion/CompositeShadingLanguageVersion.java +++ b/ast/src/main/java/com/graphicsfuzz/common/glslversion/CompositeShadingLanguageVersion.java @@ -337,4 +337,9 @@ public boolean supportedShaderMemoryControlFunctions() { public boolean supportedPushConstants() { return prototype.supportedPushConstants(); } + + @Override + public boolean supportedScalarSwizzle() { + return prototype.supportedScalarSwizzle(); + } } diff --git a/ast/src/main/java/com/graphicsfuzz/common/glslversion/Essl100.java b/ast/src/main/java/com/graphicsfuzz/common/glslversion/Essl100.java index 9c4ccd958..99e243d4b 100644 --- a/ast/src/main/java/com/graphicsfuzz/common/glslversion/Essl100.java +++ b/ast/src/main/java/com/graphicsfuzz/common/glslversion/Essl100.java @@ -338,4 +338,9 @@ public boolean supportedShaderMemoryControlFunctions() { public boolean supportedPushConstants() { return false; } + + @Override + public boolean supportedScalarSwizzle() { + return false; + } } diff --git a/ast/src/main/java/com/graphicsfuzz/common/glslversion/Glsl110.java b/ast/src/main/java/com/graphicsfuzz/common/glslversion/Glsl110.java index 0a3f770cf..2800d0be9 100644 --- a/ast/src/main/java/com/graphicsfuzz/common/glslversion/Glsl110.java +++ b/ast/src/main/java/com/graphicsfuzz/common/glslversion/Glsl110.java @@ -341,4 +341,9 @@ public boolean supportedShaderMemoryControlFunctions() { public boolean supportedPushConstants() { return false; } + + @Override + public boolean supportedScalarSwizzle() { + return false; + } } diff --git a/ast/src/main/java/com/graphicsfuzz/common/glslversion/Glsl420.java b/ast/src/main/java/com/graphicsfuzz/common/glslversion/Glsl420.java index e80958917..834b8c42d 100644 --- a/ast/src/main/java/com/graphicsfuzz/common/glslversion/Glsl420.java +++ b/ast/src/main/java/com/graphicsfuzz/common/glslversion/Glsl420.java @@ -59,4 +59,10 @@ public boolean supportedUnpackSnorm2x16() { public boolean supportedGlFragColor() { return false; } + + @Override + public boolean supportedScalarSwizzle() { + return true; + } + } diff --git a/ast/src/main/java/com/graphicsfuzz/common/glslversion/ShadingLanguageVersion.java b/ast/src/main/java/com/graphicsfuzz/common/glslversion/ShadingLanguageVersion.java index c7ad4bb5c..8bd7973c1 100644 --- a/ast/src/main/java/com/graphicsfuzz/common/glslversion/ShadingLanguageVersion.java +++ b/ast/src/main/java/com/graphicsfuzz/common/glslversion/ShadingLanguageVersion.java @@ -374,4 +374,10 @@ static ShadingLanguageVersion webGlFromVersionString(String versionString) { * @return true if and only if push constants are supported. */ boolean supportedPushConstants(); + + /** + * GLSL versions 4.2+ support scalar swizzles, such as v.x.x + * @return true if and only if scalar swizzles are supported. + */ + boolean supportedScalarSwizzle(); } diff --git a/ast/src/test/java/com/graphicsfuzz/common/typing/TyperTest.java b/ast/src/test/java/com/graphicsfuzz/common/typing/TyperTest.java index d39e904b8..10756aff5 100644 --- a/ast/src/test/java/com/graphicsfuzz/common/typing/TyperTest.java +++ b/ast/src/test/java/com/graphicsfuzz/common/typing/TyperTest.java @@ -1000,6 +1000,56 @@ public void visitBinaryExpr(BinaryExpr binaryExpr) { }.visit(tu); } + @Test + public void testScalarSwizzleTyped() throws Exception { + final TranslationUnit tu = ParseHelper.parse("#version 420\n" + + "void main() {\n" + + " float f;\n" + + " vec3 fv;\n" + + " int i;\n" + + " ivec3 iv;\n" + + " uint u;\n" + + " uvec3 uv;\n" + + " bool b;\n" + + " bvec3 bv;\n" + + " f.x;\n" + + " f.r;\n" + + " f.s;\n" + + " fv.x.x;\n" + + " fv.g.g;\n" + + " fv.t.t;\n" + + " i.x;\n" + + " i.r;\n" + + " i.s;\n" + + " iv.x.x;\n" + + " iv.g.g;\n" + + " iv.t.t;\n" + + " u.x;\n" + + " u.r;\n" + + " u.s;\n" + + " uv.x.x;\n" + + " uv.g.g;\n" + + " uv.t.t;\n" + + " b.x;\n" + + " b.r;\n" + + " b.s;\n" + + " bv.x.x;\n" + + " bv.g.g;\n" + + " bv.t.t;\n" + + "}\n"); + new NullCheckTyper(tu) { + private int counter = 0; + @Override + public void visitMemberLookupExpr(MemberLookupExpr memberLookupExpr) { + super.visitMemberLookupExpr(memberLookupExpr); + assertTrue(Arrays.asList("x", "r", "g", "s", "t").contains(memberLookupExpr.getMember())); + final BasicType type = + (BasicType) lookupType(memberLookupExpr.getStructure()).getWithoutQualifiers(); + assertSame(type.getElementType(), lookupType(memberLookupExpr)); + } + }.visit(tu); + } + private void checkComputeShaderBuiltin(String builtin, String builtinConstant, BasicType baseType, TypeQualifier qualifier) throws IOException, ParseTimeoutException, InterruptedException, GlslParserException { diff --git a/build/travis/check_headers.py b/build/travis/check_headers.py index 9e8805ce4..cf08e1dcc 100644 --- a/build/travis/check_headers.py +++ b/build/travis/check_headers.py @@ -138,7 +138,7 @@ def exclude_filename(f: str): def go(): fail = False - copyright_pattern = re.compile(r"Copyright 20(18|19|20|21) The GraphicsFuzz Project Authors") + copyright_pattern = re.compile(r"Copyright 20(18|19|20|21|22) The GraphicsFuzz Project Authors") generated_pattern = re.compile(r"(g|G)enerated") for (dirpath, dirnames, filenames) in os.walk(os.curdir, topdown=True): diff --git a/reducer/src/main/java/com/graphicsfuzz/reducer/ReductionDriver.java b/reducer/src/main/java/com/graphicsfuzz/reducer/ReductionDriver.java index d09ce92d6..d992f4ac3 100755 --- a/reducer/src/main/java/com/graphicsfuzz/reducer/ReductionDriver.java +++ b/reducer/src/main/java/com/graphicsfuzz/reducer/ReductionDriver.java @@ -126,7 +126,10 @@ private static IReductionPassManager getDefaultPassManager( IReductionOpportunityFinder.redundantUniformMetadataFinder(), IReductionOpportunityFinder.variableDeclToExprFinder(), IReductionOpportunityFinder.globalVariableDeclToExprFinder(), - IReductionOpportunityFinder.globalPrecisionDeclarationFinder())) { + IReductionOpportunityFinder.globalPrecisionDeclarationFinder(), + IReductionOpportunityFinder.removeSwizzleFinder(), + IReductionOpportunityFinder.shortenSwizzleFinder(), + IReductionOpportunityFinder.simplifySwizzleFinder())) { cleanupPasses.add(new SystematicReductionPass(context, verbose, finder)); diff --git a/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/IReductionOpportunityFinder.java b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/IReductionOpportunityFinder.java index 9bcaef3d9..11f654994 100755 --- a/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/IReductionOpportunityFinder.java +++ b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/IReductionOpportunityFinder.java @@ -593,4 +593,49 @@ public String getName() { }; } + static IReductionOpportunityFinder removeSwizzleFinder() { + return new IReductionOpportunityFinder() { + @Override + public List findOpportunities(ShaderJob shaderJob, + ReducerContext context) { + return RemoveSwizzleReductionOpportunities.findOpportunities(shaderJob, context); + } + + @Override + public String getName() { + return "removeSwizzle"; + } + }; + } + + static IReductionOpportunityFinder simplifySwizzleFinder() { + return new IReductionOpportunityFinder() { + @Override + public List findOpportunities(ShaderJob shaderJob, + ReducerContext context) { + return SimplifySwizzleReductionOpportunities.findOpportunities(shaderJob, context); + } + + @Override + public String getName() { + return "simplifySwizzle"; + } + }; + } + + static IReductionOpportunityFinder shortenSwizzleFinder() { + return new IReductionOpportunityFinder() { + @Override + public List findOpportunities(ShaderJob shaderJob, + ReducerContext context) { + return ShortenSwizzleReductionOpportunities.findOpportunities(shaderJob, context); + } + + @Override + public String getName() { + return "shortenSwizzle"; + } + }; + } + } diff --git a/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/ReductionOpportunities.java b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/ReductionOpportunities.java index 06d30e696..7b741cb67 100755 --- a/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/ReductionOpportunities.java +++ b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/ReductionOpportunities.java @@ -71,7 +71,10 @@ public static List getReductionOpportunities( IReductionOpportunityFinder.redundantUniformMetadataFinder(), IReductionOpportunityFinder.variableDeclToExprFinder(), IReductionOpportunityFinder.switchToLoopFinder(), - IReductionOpportunityFinder.globalVariableDeclToExprFinder())) { + IReductionOpportunityFinder.globalVariableDeclToExprFinder(), + IReductionOpportunityFinder.shortenSwizzleFinder(), + IReductionOpportunityFinder.removeSwizzleFinder(), + IReductionOpportunityFinder.simplifySwizzleFinder())) { final List currentOpportunities = ros .findOpportunities(shaderJob, context); if (ReductionDriver.DEBUG_REDUCER) { diff --git a/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/RemoveSwizzleReductionOpportunities.java b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/RemoveSwizzleReductionOpportunities.java new file mode 100644 index 000000000..c085c660b --- /dev/null +++ b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/RemoveSwizzleReductionOpportunities.java @@ -0,0 +1,97 @@ +/* + * Copyright 2022 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.reducer.reductionopportunities; + +import com.graphicsfuzz.common.ast.IAstNode; +import com.graphicsfuzz.common.ast.TranslationUnit; +import com.graphicsfuzz.common.ast.expr.MemberLookupExpr; +import com.graphicsfuzz.common.ast.type.BasicType; +import com.graphicsfuzz.common.transformreduce.ShaderJob; +import com.graphicsfuzz.common.typing.Typer; +import com.graphicsfuzz.common.util.ListConcat; +import java.util.Collections; +import java.util.List; + +/** + *

This class finds opportunities to remove redundant swizzles in swizzle chains.

+ */ +public class RemoveSwizzleReductionOpportunities + extends ReductionOpportunitiesBase { + + private final Typer typer; + + static List findOpportunities( + ShaderJob shaderJob, + ReducerContext context) { + return shaderJob.getShaders() + .stream() + .map(item -> findOpportunitiesForShader(item, context)) + .reduce(Collections.emptyList(), ListConcat::concatenate); + } + + private static List findOpportunitiesForShader( + TranslationUnit tu, + ReducerContext context) { + RemoveSwizzleReductionOpportunities finder = + new RemoveSwizzleReductionOpportunities(tu, context); + finder.visit(tu); + return finder.getOpportunities(); + } + + private RemoveSwizzleReductionOpportunities(TranslationUnit tu, + ReducerContext context) { + super(tu, context); + this.typer = new Typer(tu); + } + + @Override + public void visitTranslationUnit(TranslationUnit translationUnit) { + + if (!context.reduceEverywhere()) { + // This class finds semantics-changing reduction opportunities, so we cannot use it unless we + // are reducing everywhere. + return; + } + + super.visitTranslationUnit(translationUnit); + } + + @Override + public void visitMemberLookupExpr(MemberLookupExpr memberLookupExpr) { + super.visitMemberLookupExpr(memberLookupExpr); + if (!RemoveSwizzleReductionOpportunity.isSwizzle(memberLookupExpr, typer)) { + return; + } + final IAstNode parent = parentMap.getParent(memberLookupExpr); + if (typer.lookupType(memberLookupExpr) == typer.lookupType(memberLookupExpr.getStructure()) + .getWithoutQualifiers()) { + addOpportunity(new RemoveSwizzleReductionOpportunity(parent, + memberLookupExpr.getStructure(), memberLookupExpr, typer, getVistitationDepth())); + return; + } + if (!RemoveSwizzleReductionOpportunity.isSwizzle(parent, typer)) { + return; + } + final BasicType basicType = + (BasicType) typer.lookupType(memberLookupExpr.getStructure()).getWithoutQualifiers(); + if (basicType.getNumElements() > RemoveSwizzleReductionOpportunity + .getLargestSwizzleComponent(parent)) { + addOpportunity(new RemoveSwizzleReductionOpportunity(parent, + memberLookupExpr.getStructure(), memberLookupExpr, typer, getVistitationDepth())); + } + } +} diff --git a/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/RemoveSwizzleReductionOpportunity.java b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/RemoveSwizzleReductionOpportunity.java new file mode 100644 index 000000000..15e6fc062 --- /dev/null +++ b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/RemoveSwizzleReductionOpportunity.java @@ -0,0 +1,143 @@ +/* + * Copyright 2022 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.reducer.reductionopportunities; + +import com.graphicsfuzz.common.ast.IAstNode; +import com.graphicsfuzz.common.ast.expr.Expr; +import com.graphicsfuzz.common.ast.expr.MemberLookupExpr; +import com.graphicsfuzz.common.ast.type.BasicType; +import com.graphicsfuzz.common.ast.type.Type; +import com.graphicsfuzz.common.ast.visitors.VisitationDepth; +import com.graphicsfuzz.common.typing.Typer; + +/** + * Remove a needless swizzle in a chain of swizzles. For instance, simplify v.xx.xy to v.xy. The + * reduction opportunity is not semantics-preserving. + */ +public class RemoveSwizzleReductionOpportunity extends AbstractReductionOpportunity { + + private final IAstNode parent; + private final Expr newChild; + private final Expr originalChild; + + // This captures type information about the translation unit when the opportunity was created. + private final Typer typer; + + RemoveSwizzleReductionOpportunity(IAstNode parent, Expr newChild, Expr originalChild, + Typer typer, + VisitationDepth depth) { + super(depth); + this.parent = parent; + this.newChild = newChild; + this.originalChild = originalChild; + this.typer = typer; + } + + @Override + void applyReductionImpl() { + parent.replaceChild(originalChild, newChild); + } + + @Override + public boolean preconditionHolds() { + if (!parent.hasChild(originalChild)) { + return false; + } + if (!(originalChild instanceof MemberLookupExpr)) { + return false; + } + assert typer.lookupType(originalChild) instanceof BasicType; + if (typer.lookupType(originalChild) == typer.lookupType(newChild)) { + return true; + } + if (!isSwizzle(parent, typer)) { + return false; + } + final Type newChildType = typer.lookupType(newChild).getWithoutQualifiers(); + if (!(newChildType instanceof BasicType)) { + return false; + } + final BasicType newChildBasicType = (BasicType) newChildType; + if (!BasicType.allVectorTypes().contains(newChildBasicType) + && !BasicType.allScalarTypes().contains(newChildBasicType)) { + return false; + } + final int largestSwizzleComponent = getLargestSwizzleComponent(parent); + if (largestSwizzleComponent >= newChildBasicType.getNumElements()) { + return false; + } + return true; + } + + private static int swizzleCharacterToInt(char swizzleChar) { + switch (swizzleChar) { + case 'x': + case 'r': + case 's': + return 0; + case 'y': + case 'g': + case 't': + return 1; + case 'z': + case 'b': + case 'p': + return 2; + case 'w': + case 'a': + case 'q': + return 3; + default: + throw new IllegalArgumentException("Unknown swizzle character " + swizzleChar); + } + } + + static int getLargestSwizzleComponent(IAstNode node) { + assert node instanceof MemberLookupExpr; + final String swizzleCharacters = ((MemberLookupExpr) node).getMember(); + int result = swizzleCharacterToInt(swizzleCharacters.charAt(0)); + for (int i = 1; i < swizzleCharacters.length(); i++) { + int candidate = swizzleCharacterToInt(swizzleCharacters.charAt(i)); + if (candidate > result) { + result = candidate; + } + } + return result; + } + + static boolean isSwizzle(IAstNode node, Typer typer) { + if (!(node instanceof MemberLookupExpr)) { + return false; + } + final MemberLookupExpr memberLookupExpr = (MemberLookupExpr) node; + final Type structureType = + typer.lookupType(memberLookupExpr.getStructure()).getWithoutQualifiers(); + if (!(structureType instanceof BasicType)) { + return false; + } + final BasicType basicType = (BasicType) structureType; + if (!BasicType.allVectorTypes().contains(basicType) + && !BasicType.allScalarTypes().contains(basicType)) { + return false; + } + // A member lookup on a vector or scalar type has to be a swizzle. We assume that the shader is + // well-formed, so there is no need to go and check that the letters used in the swizzle are + // valid. + return true; + } + +} diff --git a/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/ShortenSwizzleReductionOpportunities.java b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/ShortenSwizzleReductionOpportunities.java new file mode 100644 index 000000000..4138e0187 --- /dev/null +++ b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/ShortenSwizzleReductionOpportunities.java @@ -0,0 +1,93 @@ +/* + * Copyright 2022 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.reducer.reductionopportunities; + +import com.graphicsfuzz.common.ast.IAstNode; +import com.graphicsfuzz.common.ast.TranslationUnit; +import com.graphicsfuzz.common.ast.expr.MemberLookupExpr; +import com.graphicsfuzz.common.transformreduce.ShaderJob; +import com.graphicsfuzz.common.typing.Typer; +import com.graphicsfuzz.common.util.ListConcat; +import java.util.Collections; +import java.util.List; + +/** + *

This class finds opportunities to make swizzles (and vector lookups) simpler.

+ */ +public class ShortenSwizzleReductionOpportunities + extends ReductionOpportunitiesBase { + + private final Typer typer; + private final TranslationUnit tu; + + static List findOpportunities( + ShaderJob shaderJob, + ReducerContext context) { + return shaderJob.getShaders() + .stream() + .map(item -> findOpportunitiesForShader(item, context)) + .reduce(Collections.emptyList(), ListConcat::concatenate); + } + + private static List findOpportunitiesForShader( + TranslationUnit tu, + ReducerContext context) { + ShortenSwizzleReductionOpportunities finder = + new ShortenSwizzleReductionOpportunities(tu, context); + finder.visit(tu); + return finder.getOpportunities(); + } + + private ShortenSwizzleReductionOpportunities(TranslationUnit tu, + ReducerContext context) { + super(tu, context); + this.typer = new Typer(tu); + this.tu = tu; + } + + @Override + public void visitTranslationUnit(TranslationUnit translationUnit) { + + if (!context.reduceEverywhere()) { + // This class finds semantics-changing reduction opportunities, so we cannot use it unless we + // are reducing everywhere. + return; + } + + super.visitTranslationUnit(translationUnit); + } + + @Override + public void visitMemberLookupExpr(MemberLookupExpr memberLookupExpr) { + super.visitMemberLookupExpr(memberLookupExpr); + if (!RemoveSwizzleReductionOpportunity.isSwizzle(memberLookupExpr, typer)) { + return; + } + final IAstNode parent = parentMap.getParent(memberLookupExpr); + if (!RemoveSwizzleReductionOpportunity.isSwizzle(parent, typer)) { + return; + } + final int minimumLength = tu.getShadingLanguageVersion().supportedScalarSwizzle() ? 1 : 2; + for (int newLength = Math.max(minimumLength, + RemoveSwizzleReductionOpportunity.getLargestSwizzleComponent(parent) + 1); + newLength < memberLookupExpr.getMember().length(); newLength++) { + addOpportunity(new ShortenSwizzleReductionOpportunity((MemberLookupExpr) parent, + memberLookupExpr, newLength, + getVistitationDepth())); + } + } +} diff --git a/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/ShortenSwizzleReductionOpportunity.java b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/ShortenSwizzleReductionOpportunity.java new file mode 100644 index 000000000..eeb6c57f3 --- /dev/null +++ b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/ShortenSwizzleReductionOpportunity.java @@ -0,0 +1,62 @@ +/* + * Copyright 2022 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.reducer.reductionopportunities; + +import com.graphicsfuzz.common.ast.expr.MemberLookupExpr; +import com.graphicsfuzz.common.ast.visitors.VisitationDepth; + +/** + * Shorten an intermediate swizzle in a chain of swizzle operations, e.g. simplify v.xyz.xx to + * v.xy.xx. The reduction opportunity is not semantics-preserving. + */ +public class ShortenSwizzleReductionOpportunity extends AbstractReductionOpportunity { + + private final MemberLookupExpr parentSwizzle; + private final MemberLookupExpr childSwizzle; + private final int newChildLength; + + ShortenSwizzleReductionOpportunity(MemberLookupExpr parentSwizzle, + MemberLookupExpr childSwizzle, + int newChildLength, + VisitationDepth depth) { + super(depth); + this.parentSwizzle = parentSwizzle; + this.childSwizzle = childSwizzle; + this.newChildLength = newChildLength; + } + + @Override + void applyReductionImpl() { + childSwizzle.setMember(childSwizzle.getMember().substring(0, newChildLength)); + } + + @Override + public boolean preconditionHolds() { + if (!parentSwizzle.hasChild(childSwizzle)) { + return false; + } + if (childSwizzle.getMember().length() <= newChildLength) { + return false; + } + if (RemoveSwizzleReductionOpportunity.getLargestSwizzleComponent(parentSwizzle) + >= newChildLength) { + return false; + } + return true; + } + +} diff --git a/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/SimplifySwizzleReductionOpportunities.java b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/SimplifySwizzleReductionOpportunities.java new file mode 100644 index 000000000..8f889ae61 --- /dev/null +++ b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/SimplifySwizzleReductionOpportunities.java @@ -0,0 +1,133 @@ +/* + * Copyright 2022 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.reducer.reductionopportunities; + +import com.graphicsfuzz.common.ast.TranslationUnit; +import com.graphicsfuzz.common.ast.expr.MemberLookupExpr; +import com.graphicsfuzz.common.transformreduce.ShaderJob; +import com.graphicsfuzz.common.typing.Typer; +import com.graphicsfuzz.common.util.ListConcat; +import java.util.Collections; +import java.util.Deque; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + *

This class finds opportunities to make swizzles (and vector lookups) simpler.

+ */ +public class SimplifySwizzleReductionOpportunities + extends ReductionOpportunitiesBase { + + private final Typer typer; + + static List findOpportunities( + ShaderJob shaderJob, + ReducerContext context) { + return shaderJob.getShaders() + .stream() + .map(item -> findOpportunitiesForShader(item, context)) + .reduce(Collections.emptyList(), ListConcat::concatenate); + } + + private static List findOpportunitiesForShader( + TranslationUnit tu, + ReducerContext context) { + SimplifySwizzleReductionOpportunities finder = + new SimplifySwizzleReductionOpportunities(tu, context); + finder.visit(tu); + return finder.getOpportunities(); + } + + private SimplifySwizzleReductionOpportunities(TranslationUnit tu, + ReducerContext context) { + super(tu, context); + this.typer = new Typer(tu); + } + + @Override + public void visitTranslationUnit(TranslationUnit translationUnit) { + + if (!context.reduceEverywhere()) { + // This class finds semantics-changing reduction opportunities, so we cannot use it unless we + // are reducing everywhere. + return; + } + + super.visitTranslationUnit(translationUnit); + } + + @Override + public void visitMemberLookupExpr(MemberLookupExpr memberLookupExpr) { + super.visitMemberLookupExpr(memberLookupExpr); + if (!RemoveSwizzleReductionOpportunity.isSwizzle(memberLookupExpr, typer)) { + return; + } + final Set existingComponents = new HashSet<>(); + for (int i = 0; i < memberLookupExpr.getMember().length(); i++) { + existingComponents.add(memberLookupExpr.getMember().charAt(i)); + } + for (int component = 0; component < memberLookupExpr.getMember().length(); component++) { + char currentComponent = memberLookupExpr.getMember().charAt(component); + // We push replacements on to a queue such that the most aggressive replacements are pushed + // last, then pop them in LIFO order. This ensures that e.g. changing 'w' to 'x' is added as + // an opportunity before changing 'w' to 'z'. + final Deque replacements = new LinkedList<>(); + switch (currentComponent) { + case 'w': + replacements.push('z'); + // fall through + case 'z': + replacements.push('y'); + // fall through + case 'y': + replacements.push('x'); + break; + case 'a': + replacements.push('b'); + // fall through + case 'b': + replacements.push('g'); + // fall through + case 'g': + replacements.push('r'); + break; + case 'q': + replacements.push('p'); + // fall through + case 'p': + replacements.push('t'); + // fall through + case 't': + replacements.push('s'); + break; + default: + break; + } + if (inLValueContext()) { + // An l-value swizzle cannot have repeated components, so we remove any existing components + // from the set of replacements. + replacements.removeAll(existingComponents); + } + while (!replacements.isEmpty()) { + addOpportunity(new SimplifySwizzleReductionOpportunity(memberLookupExpr, component, + replacements.pop(), inLValueContext(), getVistitationDepth())); + } + } + } +} diff --git a/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/SimplifySwizzleReductionOpportunity.java b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/SimplifySwizzleReductionOpportunity.java new file mode 100644 index 000000000..f92f4fab8 --- /dev/null +++ b/reducer/src/main/java/com/graphicsfuzz/reducer/reductionopportunities/SimplifySwizzleReductionOpportunity.java @@ -0,0 +1,104 @@ +/* + * Copyright 2022 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.reducer.reductionopportunities; + +import com.graphicsfuzz.common.ast.expr.MemberLookupExpr; +import com.graphicsfuzz.common.ast.visitors.VisitationDepth; + +/** + * Change a swizzle operation so that it uses "lower" components, e.g. simplifying v.xy to v.xx. The + * reduction opportunity is not semantics-preserving. + */ +public class SimplifySwizzleReductionOpportunity extends AbstractReductionOpportunity { + + final MemberLookupExpr swizzle; + final int component; + final char newComponentValue; + final boolean swizzleIsLvalue; + + SimplifySwizzleReductionOpportunity(MemberLookupExpr swizzle, + int component, char newComponentValue, + boolean swizzleIsLvalue, + VisitationDepth depth) { + super(depth); + this.swizzle = swizzle; + this.component = component; + this.newComponentValue = newComponentValue; + this.swizzleIsLvalue = swizzleIsLvalue; + } + + @Override + void applyReductionImpl() { + final StringBuilder newSwizzleComponents = new StringBuilder(); + for (int i = 0; i < swizzle.getMember().length(); i++) { + if (i == component) { + newSwizzleComponents.append(newComponentValue); + } else { + newSwizzleComponents.append(swizzle.getMember().charAt(i)); + } + } + swizzle.setMember(newSwizzleComponents.toString()); + } + + @Override + public boolean preconditionHolds() { + if (swizzle.getMember().length() <= component) { + return false; + } + + if (swizzleIsLvalue) { + for (int i = 0; i < swizzle.getMember().length(); i++) { + if (i == component) { + continue; + } + if (newComponentValue == swizzle.getMember().charAt(i)) { + // Changing the component to the new value would lead to duplicated components, which is + // not allowed in an l-value swizzle. + return false; + } + } + } + + final char existingComponentValue = swizzle.getMember().charAt(component); + switch (newComponentValue) { + case 'x': + return existingComponentValue == 'y' || existingComponentValue == 'z' + || existingComponentValue == 'w'; + case 'y': + return existingComponentValue == 'z' || existingComponentValue == 'w'; + case 'z': + return existingComponentValue == 'w'; + case 'r': + return existingComponentValue == 'g' || existingComponentValue == 'b' + || existingComponentValue == 'a'; + case 'g': + return existingComponentValue == 'b' || existingComponentValue == 'a'; + case 'b': + return existingComponentValue == 'a'; + case 's': + return existingComponentValue == 't' || existingComponentValue == 'p' + || existingComponentValue == 'q'; + case 't': + return existingComponentValue == 'p' || existingComponentValue == 'q'; + case 'p': + return existingComponentValue == 'q'; + default: + throw new IllegalArgumentException("Badd swizzle simplification."); + } + } + +} diff --git a/reducer/src/test/java/com/graphicsfuzz/reducer/reductionopportunities/RemoveSwizzleReductionOpportunitiesTest.java b/reducer/src/test/java/com/graphicsfuzz/reducer/reductionopportunities/RemoveSwizzleReductionOpportunitiesTest.java new file mode 100644 index 000000000..e23e6b0c8 --- /dev/null +++ b/reducer/src/test/java/com/graphicsfuzz/reducer/reductionopportunities/RemoveSwizzleReductionOpportunitiesTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022 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.reducer.reductionopportunities; + +import static org.junit.Assert.assertEquals; + +import com.graphicsfuzz.common.ast.TranslationUnit; +import com.graphicsfuzz.common.glslversion.ShadingLanguageVersion; +import com.graphicsfuzz.common.util.CompareAsts; +import com.graphicsfuzz.common.util.IdGenerator; +import com.graphicsfuzz.common.util.ParseHelper; +import com.graphicsfuzz.common.util.RandomWrapper; +import java.util.List; +import org.junit.Test; + +public class RemoveSwizzleReductionOpportunitiesTest { + + @Test + public void testBasic() throws Exception { + final String program = "#version 310 es\n" + + "void main() {\n" + + " vec2 v = vec2(1.0);\n" + + " v.xyz.xz.x;\n" + + "}\n"; + final String step1ReducedProgram = "#version 310 es\n" + + "void main() {\n" + + " vec2 v = vec2(1.0);\n" + + " v.xyz.x;\n" + + "}\n"; + final String step2ReducedProgram = "#version 310 es\n" + + "void main() {\n" + + " vec2 v = vec2(1.0);\n" + + " v.x;\n" + + "}\n"; + final TranslationUnit tu = ParseHelper.parse(program); + List ops = RemoveSwizzleReductionOpportunities + .findOpportunities(MakeShaderJobFromFragmentShader.make(tu), + new ReducerContext(true, true, ShadingLanguageVersion.ESSL_100, + new RandomWrapper(0), new IdGenerator())); + assertEquals(1, ops.size()); + ops.get(0).applyReduction(); + CompareAsts.assertEqualAsts(step1ReducedProgram, tu); + ops = RemoveSwizzleReductionOpportunities + .findOpportunities(MakeShaderJobFromFragmentShader.make(tu), + new ReducerContext(true, true, ShadingLanguageVersion.ESSL_100, + new RandomWrapper(0), new IdGenerator())); + assertEquals(1, ops.size()); + ops.get(0).applyReduction(); + CompareAsts.assertEqualAsts(step2ReducedProgram, tu); + } + +} diff --git a/reducer/src/test/java/com/graphicsfuzz/reducer/reductionopportunities/ShortenSwizzleReductionOpportunitiesTest.java b/reducer/src/test/java/com/graphicsfuzz/reducer/reductionopportunities/ShortenSwizzleReductionOpportunitiesTest.java new file mode 100644 index 000000000..1546c4430 --- /dev/null +++ b/reducer/src/test/java/com/graphicsfuzz/reducer/reductionopportunities/ShortenSwizzleReductionOpportunitiesTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2022 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.reducer.reductionopportunities; + +import static org.junit.Assert.assertEquals; + +import com.graphicsfuzz.common.ast.TranslationUnit; +import com.graphicsfuzz.common.glslversion.ShadingLanguageVersion; +import com.graphicsfuzz.common.util.CompareAsts; +import com.graphicsfuzz.common.util.IdGenerator; +import com.graphicsfuzz.common.util.ParseHelper; +import com.graphicsfuzz.common.util.RandomWrapper; +import java.util.List; +import org.junit.Test; + +public class ShortenSwizzleReductionOpportunitiesTest { + + @Test + public void testBasic() throws Exception { + final String program = "#version 420\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.xxxx.rrr.t;\n" + + "}\n"; + final String reducedProgram = "#version 420\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.x.rr.t;\n" + + "}\n"; + final TranslationUnit tu = ParseHelper.parse(program); + List ops = ShortenSwizzleReductionOpportunities + .findOpportunities(MakeShaderJobFromFragmentShader.make(tu), + new ReducerContext(true, true, ShadingLanguageVersion.ESSL_100, + new RandomWrapper(0), new IdGenerator())); + assertEquals(4, ops.size()); + for (ShortenSwizzleReductionOpportunity op : ops) { + if (op.preconditionHolds()) { + op.applyReduction(); + } + } + CompareAsts.assertEqualAsts(reducedProgram, tu); + } + + @Test + public void testBasicNoScalarSwizzle() throws Exception { + final String program = "#version 310 es\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.xxxx.rrr.t;\n" + + "}\n"; + final String reducedProgram = "#version 310 es\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.xx.rr.t;\n" + + "}\n"; + final TranslationUnit tu = ParseHelper.parse(program); + List ops = ShortenSwizzleReductionOpportunities + .findOpportunities(MakeShaderJobFromFragmentShader.make(tu), + new ReducerContext(true, true, ShadingLanguageVersion.ESSL_100, + new RandomWrapper(0), new IdGenerator())); + assertEquals(3, ops.size()); + for (ShortenSwizzleReductionOpportunity op : ops) { + if (op.preconditionHolds()) { + op.applyReduction(); + } + } + CompareAsts.assertEqualAsts(reducedProgram, tu); + } + +} diff --git a/reducer/src/test/java/com/graphicsfuzz/reducer/reductionopportunities/SimplifySwizzleReductionOpportunitiesTest.java b/reducer/src/test/java/com/graphicsfuzz/reducer/reductionopportunities/SimplifySwizzleReductionOpportunitiesTest.java new file mode 100644 index 000000000..c347a8ee5 --- /dev/null +++ b/reducer/src/test/java/com/graphicsfuzz/reducer/reductionopportunities/SimplifySwizzleReductionOpportunitiesTest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2022 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.reducer.reductionopportunities; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import com.graphicsfuzz.common.ast.TranslationUnit; +import com.graphicsfuzz.common.glslversion.ShadingLanguageVersion; +import com.graphicsfuzz.common.util.CompareAsts; +import com.graphicsfuzz.common.util.IdGenerator; +import com.graphicsfuzz.common.util.ParseHelper; +import com.graphicsfuzz.common.util.RandomWrapper; +import java.util.List; +import org.junit.Test; + +public class SimplifySwizzleReductionOpportunitiesTest { + + @Test + public void testBasic() throws Exception { + final String program = "#version 310 es\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.wwww.zzz.yy.x;\n" + + "}\n"; + final String reducedProgram = "#version 310 es\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.xxxx.xxx.xx.x;\n" + + "}\n"; + final TranslationUnit tu = ParseHelper.parse(program); + List ops = SimplifySwizzleReductionOpportunities + .findOpportunities(MakeShaderJobFromFragmentShader.make(tu), + new ReducerContext(true, true, ShadingLanguageVersion.ESSL_100, + new RandomWrapper(0), new IdGenerator())); + assertEquals(20, ops.size()); + for (SimplifySwizzleReductionOpportunity op : ops) { + if (op.preconditionHolds()) { + op.applyReduction(); + } + } + CompareAsts.assertEqualAsts(reducedProgram, tu); + } + + @Test + public void testDifferentComponentLetters() throws Exception { + final String program = "#version 310 es\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.rgba.stpq.xyzw;\n" + + "}\n"; + final String reducedProgram = "#version 310 es\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.rrrr.ssss.xxxx;\n" + + "}\n"; + final TranslationUnit tu = ParseHelper.parse(program); + List ops = SimplifySwizzleReductionOpportunities + .findOpportunities(MakeShaderJobFromFragmentShader.make(tu), + new ReducerContext(true, true, ShadingLanguageVersion.ESSL_100, + new RandomWrapper(0), new IdGenerator())); + assertEquals(18, ops.size()); + for (SimplifySwizzleReductionOpportunity op : ops) { + if (op.preconditionHolds()) { + op.applyReduction(); + } + } + CompareAsts.assertEqualAsts(reducedProgram, tu); + } + + @Test + public void testLvalues() throws Exception { + final String program = "#version 310 es\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.xy = vec2(1.0);\n" + + " v.rg = vec2(1.0);\n" + + " v.st = vec2(1.0);\n" + + "}\n"; + final TranslationUnit tu = ParseHelper.parse(program); + List ops = SimplifySwizzleReductionOpportunities + .findOpportunities(MakeShaderJobFromFragmentShader.make(tu), + new ReducerContext(true, true, ShadingLanguageVersion.ESSL_100, + new RandomWrapper(0), new IdGenerator())); + assertEquals(0, ops.size()); + } + + @Test + public void testLvalues2() throws Exception { + final String program = "#version 310 es\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.yz = vec2(1.0);\n" + + "}\n"; + final String expected = "#version 310 es\n" + + "void main() {\n" + + " vec4 v = vec2(1.0);\n" + + " v.xz = vec2(1.0);\n" + + "}\n"; + final TranslationUnit tu = ParseHelper.parse(program); + List ops = SimplifySwizzleReductionOpportunities + .findOpportunities(MakeShaderJobFromFragmentShader.make(tu), + new ReducerContext(true, true, ShadingLanguageVersion.ESSL_100, + new RandomWrapper(0), new IdGenerator())); + assertEquals(2, ops.size()); + ops.get(0).applyReductionImpl(); + assertFalse(ops.get(1).preconditionHolds()); + CompareAsts.assertEqualAsts(expected, tu); + } + +}