Skip to content

Commit 6e5b2b6

Browse files
committed
Fix: Prevent removing an assignment when the RHS has a function call which (directly or indirectly) modifies a global variable.
Fixes color being removed from Shane's awesome shader: https://www.shadertoy.com/view/flj3Wm
1 parent 26747e7 commit 6e5b2b6

File tree

5 files changed

+36
-3
lines changed

5 files changed

+36
-3
lines changed

ShaderShrinker/Shrinker.Parser/Optimizations/DetectConstantsExtension.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
// </summary>
1010
// -----------------------------------------------------------------------
1111

12-
using System.Collections.Generic;
1312
using System.Linq;
1413
using Shrinker.Lexer;
1514
using Shrinker.Parser.SyntaxNodes;

ShaderShrinker/Shrinker.Parser/Optimizations/RemoveUnusedVariablesExtension.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ public static void RemoveUnusedVariables(this SyntaxNode rootNode)
4646
.Where(o => o.Name == varName).ToList();
4747
foreach (var assignment in assignments)
4848
{
49-
// If RHS of the assignment calls a function with an 'out/inout' param, it is not save to remove.
49+
// If RHS of the assignment calls a function with an 'out/inout' param, it is not safe to remove.
5050
// Instead, replace the assignment with the RHS component.
51-
if (assignment.TheTree.OfType<FunctionCallSyntaxNode>().Any(o => o.HasOutParam))
51+
if (assignment.TheTree.OfType<FunctionCallSyntaxNode>().Any(o => o.HasOutParam || o.ModifiesGlobalVariables()))
5252
{
5353
var rhs = assignment.ValueNodes.ToList();
5454
rhs.Add(new GenericSyntaxNode(new SemicolonToken()));

ShaderShrinker/Shrinker.Parser/SyntaxNodes/FunctionCallSyntaxNode.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,8 @@ private FunctionCallSyntaxNode(string name)
3737
protected override SyntaxNode CreateSelf() => new FunctionCallSyntaxNode(Name);
3838

3939
public virtual bool HasOutParam => this.Root().Children.OfType<FunctionSyntaxNodeBase>().Any(o => o.Name == Name && o.HasOutParam);
40+
41+
public bool ModifiesGlobalVariables() =>
42+
this.Root().FunctionDefinitions().FirstOrDefault(o => o.Name == Name)?.ModifiesGlobalVariables() == true;
4043
}
4144
}

ShaderShrinker/Shrinker.Parser/SyntaxNodes/FunctionDefinitionSyntaxNode.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
using System;
1313
using System.Linq;
14+
using Shrinker.Lexer;
1415

1516
namespace Shrinker.Parser.SyntaxNodes
1617
{
@@ -41,5 +42,18 @@ private FunctionDefinitionSyntaxNode()
4142
public override string UiName => $"{ReturnType} {Name}{(Params.Children.Any() ? "(...)" : "()")} {{...}}";
4243

4344
protected override SyntaxNode CreateSelf() => new FunctionDefinitionSyntaxNode { ReturnType = ReturnType };
45+
46+
public bool ModifiesGlobalVariables()
47+
{
48+
var globals = this.GlobalVariables().Select(o => o.Name).ToList();
49+
var theTree = Braces.TheTree.ToList();
50+
51+
// Check self.
52+
if (theTree.Any(o => globals.Any(g => o.Token?.Content?.StartsWithVarName(g) == true)))
53+
return true;
54+
55+
// Check any calls made by self...
56+
return theTree.OfType<FunctionCallSyntaxNode>().Any(o => o.ModifiesGlobalVariables());
57+
}
4458
}
4559
}

ShaderShrinker/UnitTests/ShrinkerTests.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,23 @@ public void CheckVariableAssignedByFunctionCallOutParamCalledByReturnStatementIs
15971597
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(Code));
15981598
}
15991599

1600+
[Test]
1601+
public void CheckUnusedVariableAssignmentWhichCallsFunctionWhichModifiedGlobalVariableDoesNotRemoveFunctionCall()
1602+
{
1603+
const string Code = "int g; int f() { g = 1; return 1; } int ff() { return f(); } void main() { int n = ff(n); }";
1604+
1605+
var lexer = new Lexer();
1606+
lexer.Load(Code);
1607+
1608+
var options = CustomOptions.None();
1609+
options.CombineAssignmentWithSingleUse = true;
1610+
var rootNode = new Parser(lexer)
1611+
.Parse()
1612+
.Simplify(options);
1613+
1614+
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(Code));
1615+
}
1616+
16001617
[Test, Sequential]
16011618
public void CheckFindingVariablesToMakeConst(
16021619
[Values("int g = 2; int main() { return g; }",

0 commit comments

Comments
 (0)