Notes on memory allocation -------------------------- A run of the AllocationProfile shows that the compiler allocates roughly 30 megabytes of strings. From those, 20 megabytes come from LookupType. See the notes on current_container problems below on memory usage. GetNamespaces ------------- Obtaining the list of namespaces is an expensive process because Reflection.Emit does not provide a quick way of pulling the data out, and it is too slow to load it. Calling GetNamespaces on my machine (1Ghz): * Takes half a second with the standard assemblies (corlib + System); Fetching the types from it takes 0.0028650 seconds. * Loading the top 10 largest assemblies we ship with Mono makes MCS take 8 seconds to startup the first time, subsequent invocations take 2 seconds. Fetching all the types (Assembly.GetTypes ()) for all the assemblies takes 0.0069170 seconds. So pulling all the types takes very little time, maybe we should look into our Hashtable implementation to make it more optimal. This prohibits re-writting SimpleName to take advantage of knowing the namespace names in advance. Knowing the namespaces in advance would allow me to reduce the guesswork in which we are currently engaged to find a type definition. LookupTypeReflection: --------------------- With something like `System.Object', LookupTypeReflection will be called twice: once to find out that `System' is not a type and once for System.Object. This is required because System.Reflection requires that the type/nested types are not separated by a dot but by a plus sign. A nested class would be My+Class (My being the toplevel, Class the nested one). It is interesting to look at the most called lookups when bootstrapping MCS: 647 LTR: ArrayList 713 LTR: System.Globalization 822 LTR: System.Object+Expression 904 LTR: Mono.CSharp.ArrayList 976 LTR: System.Runtime.CompilerServices 999 LTR: Type 1118 LTR: System.Runtime 1208 LTR: Mono.CSharp.Type 1373 LTR: Mono.Languages 1599 LTR: System.Diagnostics 2036 LTR: System.Text 2302 LTR: System.Reflection.Emit 2515 LTR: System.Collections 4527 LTR: System.Reflection 22273 LTR: Mono.CSharp 24245 LTR: System 27005 LTR: Mono Analysis: The top 9 lookups are done for things which are not types. Mono.CSharp.Type happens to be a common lookup: the class Type used heavily in the compiler in the default namespace. RED FLAG: Then `Type' is looked up alone a lot of the time, this happens in parameter declarations and am not entirely sure that this is correct (FindType will pass to LookupInterfaceOrClass a the current_type.FullName, which for some reason is null!). This seems to be a problem with a lost piece of context during FindType. System.Object is also used a lot as a toplevel class, and we assume it will have children, we should just shortcut this. A cache: Adding a cache and adding a catch for `System.Object' to flag that it wont be the root of a hierarchy reduced the MCS bootstrap time from 10.22 seconds to 8.90 seconds. This cache is currently enabled with SIMPLE_SPEEDUP in typemanager.cs. Memory consumption went down from 74 megs to 65 megs with this change. Ideas: ------ Instead of the hack that *knows* about System.Object not having any children classes, we should just make it simple for a probe to know that there is no need for it. The use of DottedName --------------------- We could probably use a different system to represent names, like this: class Name { string simplename; Name parent; } So `System.ComponentModel' becomes: x: (System, null) y: (ComponentModel, x) The problem is that we would still need to construct the name to pass to GetType. current_container/current_namespace and the DeclSpace ----------------------------------------------------- We are storing fully qualified names in the DeclSpace instead of the node, this is because `current_namespace' (Namepsace) is not a DeclSpace like `current_container'. The reason for storing the full names today is this: namespace X { class Y { } } namespace A { class Y { } } The problem is that we only use the namespace stack to track the "prefix" for typecontainers, but they are not typecontainers themselves, so we have to use fully qualified names, because both A.X and A.Y would be entered in the toplevel type container. If we use the short names, there would be a name clash. To fix this problem, we have to make namespaces DeclSpaces. The full size, contrasted with the size that could be stored is: corlib: Size of strings held: 368901 Size of strings short: 147863 System: Size of strings held: 212677 Size of strings short: 97521 System.XML: Size of strings held: 128055 Size of strings short: 35782 System.Data: Size of strings held: 117896 Size of strings short: 36153 System.Web: Size of strings held: 194527 Size of strings short: 58064 System.Windows.Forms: Size of strings held: 220495 Size of strings short: 64923 TODO: 1. Create a "partial" emit context for each TypeContainer.. 2. EmitContext should be partially constructed. No IL Generator. interface_type review. parameter_array, line 952: `note: must be a single dimension array type'. Validate this Dead Code Elimination bugs: --------------------------- I should also resolve all the children expressions in Switch, Fixed, Using. Major tasks: ------------ Pinned and volatile require type modifiers that can not be encoded with Reflection.Emit. Properties and 17.6.3: Finish it. Implement base indexer access. readonly variables and ref/out BUGS ---- * Check for Final when overriding, if the parent is Final, then we cant allow an override. * Interface indexers I have not figured out why the Microsoft version puts an `instance' attribute, and I am not generating this `instance' attribute. Explanation: The reason for the `instance' attribute on indexers is that indexers only apply to instances * Break/Continue statements A finally block should reset the InLoop/LoopBegin/LoopEnd, as they are logically outside the scope of the loop. * Break/continue part 2. They should transfer control to the finally block if inside a try/catch block. * Method Registration and error CS111 The way we use the method registration to signal 111 is wrong. Method registration should only be used to register methodbuilders, we need an alternate method of checking for duplicates. * > // CSC sets beforefieldinit > class X { > // .cctor will be generated by compiler > public static readonly object O = new System.Object (); > public static void Main () {} > } > PENDING TASKS ------------- * Merge test 89 and test-34 * Revisit Primary-expression, as it has now been split into non-array-creation-expression and array-creation-expression. * Code cleanup The information when registering a method in InternalParameters is duplicated, you can always get the types from the InternalParameters * Emit modreq for volatiles Handle modreq from public apis. * Emit `pinned' for pinned local variables. Both `modreq' and pinned will require special hacks in the compiler. * Make sure that we are pinning the right variable * Merge tree.cs, rootcontext.cs OPTIMIZATIONS ------------- * User Defined Conversions is doing way too many calls to do union sets that are not needed * Add test case for destructors * Places that use `Ldelema' are basically places where I will be initializing a value type. I could apply an optimization to disable the implicit local temporary from being created (by using the method in New). * Dropping TypeContainer as an argument to EmitContext My theory is that I can get rid of the TypeBuilder completely from the EmitContext, and have typecasts where it is used (from DeclSpace to where it matters). The only pending problem is that the code that implements Aliases is on TypeContainer, and probably should go in DeclSpace. * Use of local temporary in UnaryMutator We should get rid of the Localtemporary there for some cases This turns out to be very complex, at least for the post-version, because this case: a = i++ To produce optimal code, it is necessary for UnaryMutator to know that it is being assigned to a variable (the way the stack is laid out using dup requires the store to happen inside UnaryMutator). * Emitcontext Do we really need to instanciate this variable all the time? It could be static for all we care, and just use it for making sure that there are no recursive invocations on it. * Tests Write tests for the various reference conversions. We have test for all the numeric conversions. * Optimizations In Indexers and Properties, probably support an EmitWithDup That emits the code to call Get and then leaves a this pointer in the stack, so that later a Store can be emitted using that this pointer (consider Property++ or Indexer++) * Optimizations: variable allocation. When local variables of a type are required, we should request the variable and later release it when we are done, so that the same local variable slot can be reused later on. * Add a cache for the various GetArrayMethod operations. * TypeManager.FindMembers: Instead of having hundreds of builder_to_blah hash table, have a single one that maps a TypeBuilder `t' to a set of classes that implement an interface that supports FindMembers. * MakeUnionSet Callers If the types are the same, there is no need to compute the unionset, we can just use the list from one of the types. * Factor the lookup code for class declarations an interfaces (interface.cs:GetInterfaceByName) RECOMMENDATIONS --------------- * Use of lexer.Location in the parser Currently we do: TOKEN nt TERMINAL nt TERMINAL nt3 { $$ = new Blah ($2, $4, $6, lexer.Location); } This is bad, because the lexer.Location is for the last item in `nt3' We need to change that to use this pattern: TOKEN { oob_stack.Push (lexer.Location) } nt TERMINAL nt TERMINAL nt3 { $$ = new Blah ($3, $5, $7, (Location) oob_stack.Pop ()); } Notice how numbering of the arguments changes as the { oob_stack.Push (lexer.Location) } takes a "slot" in the productions. * local_variable_declaration Not sure that this grammar is correct, we might have to resolve this during semantic analysis.