PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Microsoft.Scripting.Core/Compiler/CompilerScope.cs

https://bitbucket.org/stefanrusek/xronos
C# | 468 lines | 275 code | 68 blank | 125 comment | 62 complexity | f67f2b936590a32c3f7bd966b820ad54 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System; using Microsoft;
  16. using System.Collections.Generic;
  17. using System.Collections.ObjectModel;
  18. using System.Diagnostics;
  19. using System.Reflection.Emit;
  20. using System.Runtime.CompilerServices;
  21. #if !CODEPLEX_40
  22. using Microsoft.Runtime.CompilerServices;
  23. #endif
  24. #if CODEPLEX_40
  25. using System.Dynamic;
  26. using System.Dynamic.Utils;
  27. #else
  28. using Microsoft.Scripting;
  29. using Microsoft.Scripting.Utils;
  30. #endif
  31. #if CODEPLEX_40
  32. namespace System.Linq.Expressions.Compiler {
  33. #else
  34. namespace Microsoft.Linq.Expressions.Compiler {
  35. #endif
  36. internal enum VariableStorageKind {
  37. Local,
  38. Hoisted
  39. }
  40. /// <summary>
  41. /// CompilerScope is the data structure which the Compiler keeps information
  42. /// related to compiling scopes. It stores the following information:
  43. /// 1. Parent relationship (for resolving variables)
  44. /// 2. Information about hoisted variables
  45. /// 3. Information for resolving closures
  46. ///
  47. /// Instances are produced by VariableBinder, which does a tree walk
  48. /// looking for scope nodes: LambdaExpression and BlockExpression.
  49. /// </summary>
  50. internal sealed partial class CompilerScope {
  51. /// <summary>
  52. /// parent scope, if any
  53. /// </summary>
  54. private CompilerScope _parent;
  55. /// <summary>
  56. /// The expression node for this scope
  57. /// Can be LambdaExpression, BlockExpression, or CatchBlock
  58. /// </summary>
  59. internal readonly object Node;
  60. /// <summary>
  61. /// True if this node corresponds to an IL method.
  62. /// Can only be true if the Node is a LambdaExpression.
  63. /// But inlined lambdas will have it set to false.
  64. /// </summary>
  65. internal readonly bool IsMethod;
  66. /// <summary>
  67. /// Does this scope (or any inner scope) close over variables from any
  68. /// parent scope?
  69. /// Populated by VariableBinder
  70. /// </summary>
  71. internal bool NeedsClosure;
  72. /// <summary>
  73. /// Variables defined in this scope, and whether they're hoisted or not
  74. /// Populated by VariableBinder
  75. /// </summary>
  76. internal readonly Dictionary<ParameterExpression, VariableStorageKind> Definitions = new Dictionary<ParameterExpression, VariableStorageKind>();
  77. /// <summary>
  78. /// Each variable referenced within this scope, and how often it was referenced
  79. /// Populated by VariableBinder
  80. /// </summary>
  81. internal Dictionary<ParameterExpression, int> ReferenceCount;
  82. /// <summary>
  83. /// Scopes whose variables were merged into this one
  84. ///
  85. /// Created lazily as we create hundreds of compiler scopes w/o merging scopes when compiling rules.
  86. /// </summary>
  87. internal Set<object> MergedScopes;
  88. /// <summary>
  89. /// The scope's hoisted locals, if any.
  90. /// Provides storage for variables that are referenced from nested lambdas
  91. /// </summary>
  92. private HoistedLocals _hoistedLocals;
  93. /// <summary>
  94. /// The closed over hoisted locals
  95. /// </summary>
  96. private HoistedLocals _closureHoistedLocals;
  97. /// <summary>
  98. /// Mutable dictionary that maps non-hoisted variables to either local
  99. /// slots or argument slots
  100. /// </summary>
  101. private readonly Dictionary<ParameterExpression, Storage> _locals = new Dictionary<ParameterExpression, Storage>();
  102. internal CompilerScope(object node, bool isMethod) {
  103. Node = node;
  104. IsMethod = isMethod;
  105. var variables = GetVariables(node);
  106. Definitions = new Dictionary<ParameterExpression, VariableStorageKind>(variables.Count);
  107. foreach (var v in variables) {
  108. Definitions.Add(v, VariableStorageKind.Local);
  109. }
  110. }
  111. /// <summary>
  112. /// This scope's hoisted locals, or the closed over locals, if any
  113. /// Equivalent to: _hoistedLocals ?? _closureHoistedLocals
  114. /// </summary>
  115. internal HoistedLocals NearestHoistedLocals {
  116. get { return _hoistedLocals ?? _closureHoistedLocals; }
  117. }
  118. /// <summary>
  119. /// Called when entering a lambda/block. Performs all variable allocation
  120. /// needed, including creating hoisted locals and IL locals for accessing
  121. /// parent locals
  122. /// </summary>
  123. internal CompilerScope Enter(LambdaCompiler lc, CompilerScope parent) {
  124. SetParent(lc, parent);
  125. AllocateLocals(lc);
  126. if (IsMethod && _closureHoistedLocals != null) {
  127. EmitClosureAccess(lc, _closureHoistedLocals);
  128. }
  129. EmitNewHoistedLocals(lc);
  130. if (IsMethod) {
  131. EmitCachedVariables();
  132. }
  133. return this;
  134. }
  135. /// <summary>
  136. /// Frees unnamed locals, clears state associated with this compiler
  137. /// </summary>
  138. internal CompilerScope Exit() {
  139. // free scope's variables
  140. if (!IsMethod) {
  141. foreach (Storage storage in _locals.Values) {
  142. storage.FreeLocal();
  143. }
  144. }
  145. // Clear state that is associated with this parent
  146. // (because the scope can be reused in another context)
  147. CompilerScope parent = _parent;
  148. _parent = null;
  149. _hoistedLocals = null;
  150. _closureHoistedLocals = null;
  151. _locals.Clear();
  152. return parent;
  153. }
  154. #region LocalScopeExpression support
  155. internal void EmitVariableAccess(LambdaCompiler lc, ReadOnlyCollection<ParameterExpression> vars) {
  156. if (NearestHoistedLocals != null) {
  157. // Find what array each variable is on & its index
  158. var indexes = new List<long>(vars.Count);
  159. foreach (var variable in vars) {
  160. // For each variable, find what array it's defined on
  161. ulong parents = 0;
  162. HoistedLocals locals = NearestHoistedLocals;
  163. while (!locals.Indexes.ContainsKey(variable)) {
  164. parents++;
  165. locals = locals.Parent;
  166. Debug.Assert(locals != null);
  167. }
  168. // combine the number of parents we walked, with the
  169. // real index of variable to get the index to emit.
  170. ulong index = (parents << 32) | (uint)locals.Indexes[variable];
  171. indexes.Add((long)index);
  172. }
  173. if (indexes.Count > 0) {
  174. EmitGet(NearestHoistedLocals.SelfVariable);
  175. lc.EmitConstantArray(indexes.ToArray());
  176. lc.IL.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", new[] { typeof(object[]), typeof(long[]) }));
  177. return;
  178. }
  179. }
  180. // No visible variables
  181. lc.IL.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", Type.EmptyTypes));
  182. return;
  183. }
  184. #endregion
  185. #region Variable access
  186. /// <summary>
  187. /// Adds a new virtual variable corresponding to an IL local
  188. /// </summary>
  189. internal void AddLocal(LambdaCompiler gen, ParameterExpression variable) {
  190. _locals.Add(variable, new LocalStorage(gen, variable));
  191. }
  192. internal void EmitGet(ParameterExpression variable) {
  193. ResolveVariable(variable).EmitLoad();
  194. }
  195. internal void EmitSet(ParameterExpression variable) {
  196. ResolveVariable(variable).EmitStore();
  197. }
  198. internal void EmitAddressOf(ParameterExpression variable) {
  199. ResolveVariable(variable).EmitAddress();
  200. }
  201. private Storage ResolveVariable(ParameterExpression variable) {
  202. return ResolveVariable(variable, NearestHoistedLocals);
  203. }
  204. /// <summary>
  205. /// Resolve a local variable in this scope or a closed over scope
  206. /// Throws if the variable is defined
  207. /// </summary>
  208. private Storage ResolveVariable(ParameterExpression variable, HoistedLocals hoistedLocals) {
  209. // Search IL locals and arguments, but only in this lambda
  210. for (CompilerScope s = this; s != null; s = s._parent) {
  211. Storage storage;
  212. if (s._locals.TryGetValue(variable, out storage)) {
  213. return storage;
  214. }
  215. // if this is a lambda, we're done
  216. if (s.IsMethod) {
  217. break;
  218. }
  219. }
  220. // search hoisted locals
  221. for (HoistedLocals h = hoistedLocals; h != null; h = h.Parent) {
  222. int index;
  223. if (h.Indexes.TryGetValue(variable, out index)) {
  224. return new ElementBoxStorage(
  225. ResolveVariable(h.SelfVariable, hoistedLocals),
  226. index,
  227. variable
  228. );
  229. }
  230. }
  231. //
  232. // If this is an unbound variable in the lambda, the error will be
  233. // thrown from VariableBinder. So an error here is generally caused
  234. // by an internal error, e.g. a scope was created but it bypassed
  235. // VariableBinder.
  236. //
  237. throw Error.UndefinedVariable(variable.Name, variable.Type, CurrentLambdaName);
  238. }
  239. #endregion
  240. private void SetParent(LambdaCompiler lc, CompilerScope parent) {
  241. Debug.Assert(_parent == null && parent != this);
  242. _parent = parent;
  243. if (NeedsClosure && _parent != null) {
  244. _closureHoistedLocals = _parent.NearestHoistedLocals;
  245. }
  246. var hoistedVars = GetVariables().Where(p => Definitions[p] == VariableStorageKind.Hoisted).ToReadOnly();
  247. if (hoistedVars.Count > 0) {
  248. _hoistedLocals = new HoistedLocals(_closureHoistedLocals, hoistedVars);
  249. AddLocal(lc, _hoistedLocals.SelfVariable);
  250. }
  251. }
  252. // Emits creation of the hoisted local storage
  253. private void EmitNewHoistedLocals(LambdaCompiler lc) {
  254. if (_hoistedLocals == null) {
  255. return;
  256. }
  257. // create the array
  258. lc.IL.EmitInt(_hoistedLocals.Variables.Count);
  259. lc.IL.Emit(OpCodes.Newarr, typeof(object));
  260. // initialize all elements
  261. int i = 0;
  262. foreach (ParameterExpression v in _hoistedLocals.Variables) {
  263. // array[i] = new StrongBox<T>(...);
  264. lc.IL.Emit(OpCodes.Dup);
  265. lc.IL.EmitInt(i++);
  266. Type boxType = typeof(StrongBox<>).MakeGenericType(v.Type);
  267. if (IsMethod && lc.Parameters.Contains(v)) {
  268. // array[i] = new StrongBox<T>(argument);
  269. int index = lc.Parameters.IndexOf(v);
  270. lc.EmitLambdaArgument(index);
  271. lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(new Type[] { v.Type }));
  272. } else if (v == _hoistedLocals.ParentVariable) {
  273. // array[i] = new StrongBox<T>(closure.Locals);
  274. ResolveVariable(v, _closureHoistedLocals).EmitLoad();
  275. lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(new Type[] { v.Type }));
  276. } else {
  277. // array[i] = new StrongBox<T>();
  278. lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(Type.EmptyTypes));
  279. }
  280. // if we want to cache this into a local, do it now
  281. if (ShouldCache(v)) {
  282. lc.IL.Emit(OpCodes.Dup);
  283. CacheBoxToLocal(lc, v);
  284. }
  285. lc.IL.Emit(OpCodes.Stelem_Ref);
  286. }
  287. // store it
  288. EmitSet(_hoistedLocals.SelfVariable);
  289. }
  290. // If hoisted variables are referenced "enough", we cache the
  291. // StrongBox<T> in an IL local, which saves an array index and a cast
  292. // when we go to look it up later
  293. private void EmitCachedVariables() {
  294. if (ReferenceCount == null) {
  295. return;
  296. }
  297. foreach (var refCount in ReferenceCount) {
  298. if (ShouldCache(refCount.Key, refCount.Value)) {
  299. var storage = ResolveVariable(refCount.Key) as ElementBoxStorage;
  300. if (storage != null) {
  301. storage.EmitLoadBox();
  302. CacheBoxToLocal(storage.Compiler, refCount.Key);
  303. }
  304. }
  305. }
  306. }
  307. private bool ShouldCache(ParameterExpression v, int refCount) {
  308. // This caching is too aggressive in the face of conditionals and
  309. // switch. Also, it is too conservative for variables used inside
  310. // of loops.
  311. return refCount > 2 && !_locals.ContainsKey(v);
  312. }
  313. private bool ShouldCache(ParameterExpression v) {
  314. if (ReferenceCount == null) {
  315. return false;
  316. }
  317. int refCount;
  318. return ReferenceCount.TryGetValue(v, out refCount) && ShouldCache(v, refCount);
  319. }
  320. private void CacheBoxToLocal(LambdaCompiler lc, ParameterExpression v) {
  321. Debug.Assert(ShouldCache(v) && !_locals.ContainsKey(v));
  322. var local = new LocalBoxStorage(lc, v);
  323. local.EmitStoreBox();
  324. _locals.Add(v, local);
  325. }
  326. // Creates IL locals for accessing closures
  327. private void EmitClosureAccess(LambdaCompiler lc, HoistedLocals locals) {
  328. if (locals == null) {
  329. return;
  330. }
  331. EmitClosureToVariable(lc, locals);
  332. while ((locals = locals.Parent) != null) {
  333. var v = locals.SelfVariable;
  334. var local = new LocalStorage(lc, v);
  335. local.EmitStore(ResolveVariable(v));
  336. _locals.Add(v, local);
  337. }
  338. }
  339. private void EmitClosureToVariable(LambdaCompiler lc, HoistedLocals locals) {
  340. lc.EmitClosureArgument();
  341. lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Locals"));
  342. AddLocal(lc, locals.SelfVariable);
  343. EmitSet(locals.SelfVariable);
  344. }
  345. // Allocates slots for IL locals or IL arguments
  346. private void AllocateLocals(LambdaCompiler lc) {
  347. foreach (ParameterExpression v in GetVariables()) {
  348. if (Definitions[v] == VariableStorageKind.Local) {
  349. //
  350. // If v is in lc.Parameters, it is a parameter.
  351. // Otherwise, it is a local variable.
  352. //
  353. // Also, for inlined lambdas we'll create a local, which
  354. // is possibly a byref local if the parameter is byref.
  355. //
  356. Storage s;
  357. if (IsMethod && lc.Parameters.Contains(v)) {
  358. s = new ArgumentStorage(lc, v);
  359. } else {
  360. s = new LocalStorage(lc, v);
  361. }
  362. _locals.Add(v, s);
  363. }
  364. }
  365. }
  366. private IList<ParameterExpression> GetVariables() {
  367. var vars = GetVariables(Node);
  368. if (MergedScopes == null) {
  369. return vars;
  370. }
  371. var list = new List<ParameterExpression>(vars);
  372. foreach (var scope in MergedScopes) {
  373. list.AddRange(GetVariables(scope));
  374. }
  375. return list;
  376. }
  377. private static IList<ParameterExpression> GetVariables(object scope) {
  378. var lambda = scope as LambdaExpression;
  379. if (lambda != null) {
  380. return lambda.Parameters;
  381. }
  382. var block = scope as BlockExpression;
  383. if (block != null) {
  384. return block.Variables;
  385. }
  386. return new[] { ((CatchBlock)scope).Variable };
  387. }
  388. private string CurrentLambdaName {
  389. get {
  390. CompilerScope s = this;
  391. while (true) {
  392. var lambda = s.Node as LambdaExpression;
  393. if (lambda != null) {
  394. return lambda.Name;
  395. }
  396. }
  397. }
  398. }
  399. }
  400. }