• Български
  • English

Implementing a simple optimization method

SolidOpt has two basic concepts - code models and transformations. Transformations are two kinds - ones that change the code model into another and ones that change the current code model. In this howto we will focus on the transformations changing the current model. Such transformations tend to make the code model "better" from certain point of view. Usually they are called optimizations. The following tutorial illustrates how to create a new optimization method using the SolidOpt Framework. The optimization method will look for dead code an eliminate it.

For example line 5 and 6 are not reachable under any circumstance in the following code snippet:

bool LessThan(int a) {
  if (a > 3)
    return false;
    return true;
  string helloStr = "Hello world";

Line 5 and 6 are called dead code, i.e. we will never see the "Hello world" string printed on console.

The shown code corresponds to the following .NET intermediate language (the full assembly code is attached below):

IL_0000:  ldarg.0 
IL_0001:  ldc.i4.3 
IL_0002:  ble IL_0013

IL_0007:  ldstr "Greater than 3."
IL_000c:  call void class [mscorlib]System.Console::WriteLine(string)
IL_0011:  ldc.i4.0 
IL_0012:  ret 
IL_0013:  ldstr "Less than 3."
IL_0018:  call void class [mscorlib]System.Console::WriteLine(string)
IL_001d:  ldc.i4.1 
IL_001e:  ret
IL_001f:  ldstr "Hello world" 
IL_0024:  stloc.0
IL_0025:  ldloc.0
IL_0026:  call void class [mscorlib]System.Console::WriteLine(string)

Line from 13 to 16 will be never executed.

The easiest way of finding dead code is to build a graph of the sequence of execution, called control flow graph. Once we have it, the dead code is the nodes that are not connected to anything. In the following couple of steps we will build a tool that opens an assembly, finds the method and writes out new, optimized, assembly.

Step1: Create New Project

Open you favorite IDE and create new console project:


Step2: Find The Right Libraries to Use

Besides System.dll you will need the following libraries from SolidOpt:

  • Mono.Cecil.dll - part of SolidOpt's vendors
  • SolidOpt.Services.Transformations.CodeModel.ControlFlowGraph.dll - The control flow graph representation code model, which will be used to iterate over.
  • SolidOpt.Services.Transformations.MultiModel.ILtoCFG.dll - The library, responsible to transform (decompile) the Intermediate Language to a Control Flow Graph.
  • SolidOpt.Services.Transformations.MultiModel.CFGtoIL.dll - The library responsible to turn the Control Flow Graph into Intermediate Language.

Step3: Build Control Flow Graph

For building a CFG you will need the ControlFlowGraph code model implemented in SolidOpt.Services.Transformations.CodeModel.ControlFlowGraph.dll and a transformation, turning IL to CFG (SolidOpt.Services.Transformations.MultiModel.ILtoCFG.dll). First we need to read the IL representation of the target method:

public static MethodDefinition GetLessThanMethodDefinition() {
	// Read the current assembly.
	var assembly = AssemblyDefinition.ReadAssembly("Example.exe");
	// Find MainClass. It is the second type in the current example.
	TypeDefinition mainClassTD = assembly.MainModule.Types[1];
	// LessThan is the first method in the class.
	return mainClassTD.Methods[0];

and then it the actual creation of the control flow graph is pretty straight-forward:

public static ControlFlowGraph BuildCFG(MethodDefinition mDef) {
	CilToControlFlowGraph transformer = new CilToControlFlowGraph();
	return transformer.Transform(mDef);

Step4: Implement A Dead Code Elimination

As we said the dead code in a control flow graph are nodes without any edges (ie disconnected nodes). In SolidOpt it will be enough to turn the control flow graph into IL. The transformation itself iterates over only the connected nodes.

public static MethodDefinition BuildMethodIL(ControlFlowGraph cfg) {
	ControlFlowGraphToCil transformer = new ControlFlowGraphToCil();
	return transformer.Transform(cfg);

Step5: Implement An Assembly Writer

Now we need to store the results in an assembly:

public static void Store(MethodDefinition mDef) {
	var assembly = AssemblyDefinition.ReadAssembly("Example.exe");
	// We know that MainClass is always the second type in the current example
	TypeDefinition mainClassTD = assembly.MainModule.Types[1];
	// LessThan is the first method in the class.
	mainClassTD.Methods[0].Body = mDef.Body;

The source code of the tutorial is attached below.

Happy hacking!

Example.il1.62 KB
DeadCodeRemover.zip211.28 KB