Skip to content

Commit 3f8a055

Browse files
committed
Feature: Variable assignment can now be merged with a following 'if' condition, if not used elsewhere.
E.g. int i = myFunc(); if (i > 2) ...; Reduced down to: if (myFunc() > 2) ...;
1 parent 91ae184 commit 3f8a055

File tree

5 files changed

+166
-52
lines changed

5 files changed

+166
-52
lines changed

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ float doMaths() {
462462

463463
---
464464
## Combine Assignment With Single Use
465-
An assignment used on the next line can often be inlined.
465+
A variable assignment used on the next line can often be inlined, if that next line is an assignment or ```if``` condition.
466466
#### Before
467467
```c
468468
float doMaths() {
@@ -481,6 +481,24 @@ float doMaths() {
481481
return c;
482482
}
483483
```
484+
Also
485+
#### Before
486+
```c
487+
bool f() {
488+
float a = getValue();
489+
if (a > 2.)
490+
return true;
491+
return false;
492+
}
493+
```
494+
#### After
495+
```c
496+
bool f() {
497+
if (getValue() > 2.)
498+
return true;
499+
return false;
500+
}
501+
```
484502

485503
---
486504
## Introduce +=, -=, /=, *=

ShaderShrinker/Shrinker.Parser/Optimizations/CombineAssignmentWithSingleUseExtension.cs

Lines changed: 93 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -66,55 +66,22 @@ public static bool CombineAssignmentWithSingleUse(this SyntaxNode rootNode)
6666
n = declParent.NextNonComment;
6767

6868
// If next operation isn't an assignment, ignore...
69-
var nextAssignment = n as VariableAssignmentSyntaxNode;
70-
if (nextAssignment == null)
71-
continue;
72-
73-
// The next assignment must be for a different variable.
74-
if (nextAssignment.Name == assignment.Name)
75-
continue;
76-
77-
// The assignment must happen in the same scope as the variable declaration.
78-
if (variableDecl.FindAncestor<BraceSyntaxNode>() != nextAssignment.FindAncestor<BraceSyntaxNode>())
79-
continue;
80-
81-
// ...and must use the variable exactly once...
82-
var nextAssignmentUsesOfVar =
83-
nextAssignment
84-
.TheTree
85-
.OfType<GenericSyntaxNode>()
86-
.Where(o => o.IsVarName(assignment.Name))
87-
.ToList();
88-
if (nextAssignmentUsesOfVar.Count != 1)
89-
continue;
90-
91-
var usage = nextAssignmentUsesOfVar.Single();
92-
93-
// Don't join if the next assignment uses a function call.
94-
// (Just in case it modifies the variable.)
95-
var intermediateNodes = nextAssignment.TheTree.TakeWhile(o => o != usage);
96-
var hasFunctionCall = intermediateNodes.OfType<FunctionCallSyntaxNode>().Any(o => o.HasOutParam);
97-
if (hasFunctionCall)
98-
continue;
99-
100-
// Inline the variable!
101-
var addBrackets = assignment.Children.Any(o => o.Token is SymbolOperatorToken);
102-
if (addBrackets)
69+
if (n is VariableAssignmentSyntaxNode nextAssignment)
10370
{
104-
usage.ReplaceWith(new RoundBracketSyntaxNode(assignment.Children));
105-
106-
// Try to remove the brackets if we can.
107-
var customOptions = CustomOptions.None();
108-
customOptions.SimplifyArithmetic = true;
109-
nextAssignment.Simplify(customOptions);
71+
if (!TryCombineWithNextAssignment(variableDecl, assignment, nextAssignment))
72+
continue;
73+
}
74+
else if (n is IfSyntaxNode ifNode)
75+
{
76+
if (!TryCombineWithNextIf(variableDecl, assignment, ifNode))
77+
continue;
11078
}
11179
else
11280
{
113-
usage.ReplaceWith(assignment.Children.ToArray());
81+
// No change made.
82+
continue;
11483
}
11584

116-
assignment.Remove();
117-
11885
// If the declaration isn't declaring any variables any more, remove it.
11986
if (declParent != null && !declParent.Children.Any())
12087
declParent.Remove();
@@ -130,5 +97,88 @@ public static bool CombineAssignmentWithSingleUse(this SyntaxNode rootNode)
13097

13198
return anyChanges;
13299
}
100+
101+
private static bool TryCombineWithNextAssignment(VariableDeclarationSyntaxNode variableDecl, VariableAssignmentSyntaxNode assignment, VariableAssignmentSyntaxNode nextAssignment)
102+
{
103+
// The next assignment must be for a different variable.
104+
if (nextAssignment.Name == assignment.Name)
105+
return false;
106+
107+
// The assignment must happen in the same scope as the variable declaration.
108+
if (variableDecl.FindAncestor<BraceSyntaxNode>() != nextAssignment.FindAncestor<BraceSyntaxNode>())
109+
return false;
110+
111+
// ...and must use the variable exactly once...
112+
var nextAssignmentUsesOfVar =
113+
nextAssignment
114+
.TheTree
115+
.OfType<GenericSyntaxNode>()
116+
.Where(o => o.IsVarName(assignment.Name))
117+
.ToList();
118+
if (nextAssignmentUsesOfVar.Count != 1)
119+
return false;
120+
121+
var usage = nextAssignmentUsesOfVar.Single();
122+
123+
// Don't join if the next assignment uses a function call.
124+
// (Just in case it modifies the variable.)
125+
var intermediateNodes = nextAssignment.TheTree.TakeWhile(o => o != usage);
126+
var hasFunctionCall = intermediateNodes.OfType<FunctionCallSyntaxNode>().Any(o => o.HasOutParam);
127+
if (hasFunctionCall)
128+
return false;
129+
130+
// Inline the variable!
131+
var addBrackets = assignment.Children.Any(o => o.Token is SymbolOperatorToken);
132+
if (addBrackets)
133+
{
134+
usage.ReplaceWith(new RoundBracketSyntaxNode(assignment.Children));
135+
136+
// Try to remove the brackets if we can.
137+
var customOptions = CustomOptions.None();
138+
customOptions.SimplifyArithmetic = true;
139+
nextAssignment.Simplify(customOptions);
140+
}
141+
else
142+
{
143+
usage.ReplaceWith(assignment.Children.ToArray());
144+
}
145+
146+
assignment.Remove();
147+
return true;
148+
}
149+
150+
private static bool TryCombineWithNextIf(VariableDeclarationSyntaxNode variableDecl, VariableAssignmentSyntaxNode assignment, IfSyntaxNode ifNode)
151+
{
152+
var conditionTree = ifNode.Conditions.TheTree;
153+
154+
// The 'if' condition must use the variable exactly once.
155+
var usages = conditionTree.OfType<GenericSyntaxNode>().Where(o => o.IsVarName(assignment.Name)).ToList();
156+
if (usages.Count != 1)
157+
return false;
158+
159+
// ...and cannot use it with an array index or vector field.
160+
if (conditionTree.OfType<GenericSyntaxNode>().Count(o => o.StartsWithVarName(assignment.Name)) != 1)
161+
return false;
162+
163+
// Don't join if the 'if' condition uses a function call.
164+
// (Just in case it modifies the variable.)
165+
var hasFunctionCall = conditionTree.OfType<FunctionCallSyntaxNode>().Any(o => o.HasOutParam);
166+
if (hasFunctionCall)
167+
return false;
168+
169+
// Inline the variable!
170+
usages.Single().ReplaceWith(assignment.Children.ToArray());
171+
assignment.Remove();
172+
173+
// Remove the declaration too?
174+
var assDef = variableDecl.Definitions.FirstOrDefault(o => o.Name == assignment.Name);
175+
if (assDef?.FindDeclarationScope().OfType<GenericSyntaxNode>().Any(o => o.StartsWithVarName(assignment.Name)) == false)
176+
assDef.Remove();
177+
178+
if (!variableDecl.Children.Any())
179+
variableDecl.Remove();
180+
181+
return true;
182+
}
133183
}
134184
}

ShaderShrinker/Shrinker.WpfApp/OptionsDialog.xaml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@
463463
<CheckBox.ToolTip>
464464
<MdXaml:MarkdownScrollViewer xml:space="preserve">
465465
### Combine Assignment With Single Use
466-
An assignment used on the next line can often be inlined.
466+
A variable assignment used on the next line can often be inlined, if that next line is an assignment or ```if``` condition.
467467
#### Before
468468
```c
469469
float doMaths() {
@@ -482,6 +482,24 @@
482482
return c;
483483
}
484484
```
485+
Also
486+
#### Before
487+
```c
488+
bool f() {
489+
float a = getValue();
490+
if (a > 2.)
491+
return true;
492+
return false;
493+
}
494+
```
495+
#### After
496+
```c
497+
bool f() {
498+
if (getValue() > 2.)
499+
return true;
500+
return false;
501+
}
502+
```
485503
</MdXaml:MarkdownScrollViewer>
486504
</CheckBox.ToolTip>
487505
</CheckBox>

ShaderShrinker/UnitTests/ShrinkerTests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,39 @@ public void CheckCombiningAssignmentWithUseNotPerformedIfVariableDeclaredInOuter
10871087
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(code));
10881088
}
10891089

1090+
[Test, Sequential]
1091+
public void CheckCombiningAssignmentWithSingleUseInIfStatement(
1092+
[Values("int main() { bool b = true; if (b) return 1; return 0; }",
1093+
"int main() { bool b = false; if (b == true) return 1; return 0; }",
1094+
"int main() { bool b = false; if (true != b) return 1; return 0; }",
1095+
"int main() { bool b = false; if (b == !b) return 1; return 0; }",
1096+
"int main() { vec2 v = vec2(1); if (v.x > 0.0) return 1; return 0; }",
1097+
"int main() { int i[2] = int[2](1, 2); if (i[0] > 0.0) return 1; return 0; }",
1098+
"int main() { int i = 2; if (i > 0) return 1; return 0; }",
1099+
"int main() { int i = 2; if (i > 0) return i; return 0; }",
1100+
"int main() { int i; i = 1 + 2; if (i >= 0) return 1; return 0; }")] string code,
1101+
[Values("int main() { if (true) return 1; return 0; }",
1102+
"int main() { if (false == true) return 1; return 0; }",
1103+
"int main() { if (true != false) return 1; return 0; }",
1104+
"int main() { bool b = false; if (b == !b) return 1; return 0; }",
1105+
"int main() { vec2 v = vec2(1); if (v.x > 0.0) return 1; return 0; }",
1106+
"int main() { int i[2] = int[2](1, 2); if (i[0] > 0.0) return 1; return 0; }",
1107+
"int main() { if (2 > 0) return 1; return 0; }",
1108+
"int main() { int i = 2; if (i > 0) return i; return 0; }",
1109+
"int main() { if (1 + 2 >= 0) return 1; return 0; }")] string expected)
1110+
{
1111+
var lexer = new Lexer();
1112+
lexer.Load(code);
1113+
1114+
var options = CustomOptions.None();
1115+
options.CombineAssignmentWithSingleUse = true;
1116+
var rootNode = new Parser(lexer)
1117+
.Parse()
1118+
.Simplify(options);
1119+
1120+
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(expected));
1121+
}
1122+
10901123
[Test, Sequential]
10911124
public void CheckCombiningConsecutiveReassignments(
10921125
[Values("int main() { int a = 1; a = a + 2; a = a - 4; return a; }",

ShaderShrinker/UnitTests/TestFiles/SimplifiedReference/ED209.glsl

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Processed by 'GLSL Shader Shrinker' (Shrunk by 946 characters)
1+
// Processed by 'GLSL Shader Shrinker' (Shrunk by 969 characters)
22
// (https://github.com/deanthecoder/GLSLShaderShrinker)
33

44
float stretch, gunsUp, gunsForward, edWalk, edTwist, edDown, edShoot, doorOpen, glow;
@@ -278,12 +278,7 @@ MarchData room(vec3 p) {
278278
doorHole = sdBox(p, frameInner + vec3(0, 0, 1)),
279279
backWall = length(p.z - 8.);
280280
r.d = min(backWall, max(length(p.z), -doorHole + .1));
281-
if (r.d == backWall) {
282-
float ocp = min(abs(sdOctogon(xy, 2.6)), abs(sdOctogon(xy, 1.9)));
283-
ocp = min(max(ocp, min(.7 - abs(xy.x + 1.2), -xy.y)), max(abs(sdOctogon(xy, 1.2)), min(xy.x, .7 - abs(xy.y))));
284-
if (ocp < .3) r.mat = vec3(.39, .57, .71);
285-
}
286-
281+
if (r.d == backWall) if (min(max(min(abs(sdOctogon(xy, 2.6)), abs(sdOctogon(xy, 1.9))), min(.7 - abs(xy.x + 1.2), -xy.y)), max(abs(sdOctogon(xy, 1.2)), min(xy.x, .7 - abs(xy.y)))) < .3) r.mat = vec3(.39, .57, .71);
287282
doorFrame = max(sdBox(p, frameInner + vec3(.4, .4, .1)), -doorHole);
288283
doorWidth = frameInner.x * .5;
289284
p.x -= frameInner.x;

0 commit comments

Comments
 (0)