-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDeoptimizeSwitchStatements.cs
More file actions
243 lines (201 loc) · 9.81 KB
/
Copy pathDeoptimizeSwitchStatements.cs
File metadata and controls
243 lines (201 loc) · 9.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler.ILAst;
using JSIL.Ast;
using JSIL.Internal;
using Mono.Cecil;
namespace JSIL.Transforms {
public class DeoptimizeSwitchStatements : JSAstVisitor {
public class VariableComparer : IEqualityComparer<JSVariable> {
public bool Equals (JSVariable x, JSVariable y) {
if (x == null)
return (x == y);
else
return x.Equals(y);
}
public int GetHashCode (JSVariable obj) {
return obj.GetHashCode();
}
}
public class NullCheck {
public JSIfStatement Statement;
public JSVariable SwitchVariable;
public JSGotoExpression Goto;
}
public class Initializer {
public JSIfStatement Statement;
public FieldInfo Field;
public readonly Dictionary<int, JSExpression> Values = new Dictionary<int,JSExpression>();
}
public class IndexLookup {
public JSIfStatement Statement;
public FieldInfo Field;
public JSVariable SwitchVariable;
public JSVariable OutputVariable;
public JSGotoExpression Goto;
public bool IsInverted;
}
private JSFunctionExpression CurrentFunction = null;
public readonly Dictionary<JSVariable, NullCheck> NullChecks = new Dictionary<JSVariable, NullCheck>(
new VariableComparer()
);
public readonly Dictionary<FieldInfo, Initializer> Initializers = new Dictionary<FieldInfo, Initializer>();
public readonly Dictionary<JSVariable, IndexLookup> IndexLookups = new Dictionary<JSVariable, IndexLookup>(
new VariableComparer()
);
public int SwitchStatementsDeoptimized = 0;
public readonly TypeSystem TypeSystem;
public DeoptimizeSwitchStatements (TypeSystem typeSystem) {
TypeSystem = typeSystem;
}
public void VisitNode (JSFunctionExpression fe) {
CurrentFunction = fe;
VisitChildren(fe);
}
public void VisitNode (JSIfStatement ifs) {
var boe = ifs.Condition as JSBinaryOperatorExpression;
var uoe = ifs.Condition as JSUnaryOperatorExpression;
var invocation = ifs.Condition as JSInvocationExpression;
bool invocationIsInverted = false;
if ((boe != null) && (boe.Operator == JSOperator.Equal)) {
var leftVar = boe.Left as JSVariable;
var leftIgnored = boe.Left as JSIgnoredMemberReference;
var rightNull = (JSLiteral)(boe.Right as JSDefaultValueLiteral) ?? (JSLiteral)(boe.Right as JSNullLiteral);
if (rightNull != null) {
if (leftVar != null) {
NullChecks[leftVar] = new NullCheck {
Statement = ifs,
SwitchVariable = leftVar,
Goto = ifs.AllChildrenRecursive.OfType<JSGotoExpression>().FirstOrDefault()
};
} else if (leftIgnored != null) {
var leftField = leftIgnored.Member as FieldInfo;
if (leftField != null) {
var initializer = Initializers[leftField] = new Initializer {
Field = leftField,
Statement = ifs,
};
foreach (var _invocation in ifs.TrueClause.AllChildrenRecursive.OfType<JSInvocationExpression>()) {
if (_invocation.JSMethod == null)
continue;
if (_invocation.JSMethod.Identifier != "Add")
continue;
if (_invocation.Arguments.Count != 2)
continue;
var value = _invocation.Arguments[0];
var index = _invocation.Arguments[1] as JSIntegerLiteral;
if (index == null)
continue;
initializer.Values[(int)index.Value] = value;
}
}
}
}
} else if ((uoe != null) && (uoe.Operator == JSOperator.LogicalNot)) {
invocation = uoe.Expression as JSInvocationExpression;
invocationIsInverted = true;
}
if (
(invocation != null) &&
(invocation.Arguments.Count == 2) &&
(invocation.JSMethod != null) &&
(invocation.JSMethod.Identifier == "TryGetValue")
) {
var thisIgnored = invocation.ThisReference as JSIgnoredMemberReference;
var switchVar = invocation.Arguments[0] as JSVariable;
var outRef = invocation.Arguments[1] as JSPassByReferenceExpression;
if ((thisIgnored != null) && (switchVar != null) && (outRef != null)) {
var thisField = thisIgnored.Member as FieldInfo;
var outReferent = outRef.Referent as JSReferenceExpression;
if ((thisField != null) && (outReferent != null)) {
var outVar = outReferent.Referent as JSVariable;
if (outVar != null) {
IndexLookups[outVar] = new IndexLookup {
OutputVariable = outVar,
SwitchVariable = switchVar,
Field = thisField,
Statement = ifs,
Goto = ifs.TrueClause.AllChildrenRecursive.OfType<JSGotoExpression>().FirstOrDefault(),
IsInverted = invocationIsInverted
};
if (!invocationIsInverted) {
var replacement = ifs.TrueClause;
ParentNode.ReplaceChild(ifs, replacement);
VisitReplacement(replacement);
return;
}
}
}
}
}
VisitChildren(ifs);
}
public static IEnumerable<JSGotoExpression> FindGotos (JSNode context, string targetLabel) {
return context.AllChildrenRecursive.OfType<JSGotoExpression>()
.Where((ge) => ge.TargetLabel == targetLabel);
}
public void VisitNode (JSSwitchStatement ss) {
IndexLookup indexLookup;
Initializer initializer;
NullCheck nullCheck;
// Detect switch statements using a lookup dictionary
var switchVar = ss.Condition as JSVariable;
// HACK: Fixup reference read-throughs
var switchRefRead = ss.Condition as JSReadThroughReferenceExpression;
if (switchRefRead != null)
switchVar = switchRefRead.Variable;
if (
(switchVar != null) &&
IndexLookups.TryGetValue(switchVar, out indexLookup) &&
Initializers.TryGetValue(indexLookup.Field, out initializer)
) {
if (NullChecks.TryGetValue(indexLookup.SwitchVariable, out nullCheck))
CurrentFunction.ReplaceChildRecursive(nullCheck.Statement, new JSNullStatement());
CurrentFunction.ReplaceChildRecursive(initializer.Statement, new JSNullStatement());
if (indexLookup.IsInverted)
CurrentFunction.ReplaceChildRecursive(indexLookup.Statement, new JSNullStatement());
var switchCases = new List<JSSwitchCase>();
JSExpression[] values;
foreach (var cse in ss.Cases) {
var body = cse.Body;
if (cse.Values == null) {
values = null;
body = new JSBlockStatement(body.Statements.ToArray());
} else {
values = (from v in cse.Values
let il = v as JSIntegerLiteral
where il != null
select initializer.Values[(int)il.Value]).ToArray();
}
switchCases.Add(new JSSwitchCase(
values, body, cse.IsDefault
));
}
var newSwitch = new JSSwitchStatement(
indexLookup.SwitchVariable, switchCases.ToArray()
);
SwitchStatementsDeoptimized += 1;
ParentNode.ReplaceChild(ss, newSwitch);
var outVar = indexLookup.OutputVariable;
foreach (var fn in Stack.OfType<JSFunctionExpression>()) {
foreach (var vds in fn.Body.AllChildrenRecursive.OfType<JSVariableDeclarationStatement>().ToArray()) {
for (int i = 0, c = vds.Declarations.Count; i < c; i++) {
var leftVar = vds.Declarations[i].Left as JSVariable;
if ((leftVar != null) && (leftVar.Identifier == outVar.Identifier)) {
vds.Declarations.RemoveAt(i);
i--;
c--;
}
}
}
fn.AllVariables.Remove(outVar.Identifier);
}
VisitReplacement(newSwitch);
}
VisitChildren(ss);
}
}
}