Skip to content

Instantly share code, notes, and snippets.

@ArildF
Created April 9, 2011 10:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ArildF/911288 to your computer and use it in GitHub Desktop.
Save ArildF/911288 to your computer and use it in GitHub Desktop.
Replace calls with tail calls using Mono.Cecil
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace TailCalls
{
class Program
{
static int Main(string[] args)
{
var filename = args.FirstOrDefault();
if (filename == null)
{
Usage();
return 0;
}
try
{
ProcessModule(filename);
}
catch (Exception ex)
{
Console.WriteLine(ex);
return 1;
}
return 0;
}
private static void ProcessModule(string filename)
{
var moduleDefinition = ModuleDefinition.ReadModule(filename);
var methods = from type in moduleDefinition.Types
from method in type.Methods
select method;
foreach (var methodDefinition in methods)
{
ProcessMethod(methodDefinition);
}
moduleDefinition.Write(filename);
}
private static void ProcessMethod(MethodDefinition methodDefinition)
{
var body = methodDefinition.Body;
var processor = body.GetILProcessor();
bool seenRet = false;
foreach(var instruction in body.Instructions.Reverse())
{
if (instruction.OpCode == OpCodes.Ret)
{
seenRet = true;
}
else if (seenRet && instruction.OpCode.In(OpCodes.Call, OpCodes.Calli, OpCodes.Callvirt))
{
var tailCall = processor.Create(OpCodes.Tail);
processor.InsertBefore(instruction, tailCall);
seenRet = false;
}
else if (instruction.OpCode != OpCodes.Nop)
{
seenRet = false;
}
}
}
private static void Usage()
{
Console.WriteLine(@"Usage: TailCall Path\To\Assembly.dll");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment