imHo RSSフィード

2010-05-02

あきらめて、MSILはっかそん

.NET Framework SDKCLispという、.NETのバイトコードにコンパイルするサンプルがついてる。面白そうなのでそれをいじってみる。

CLisp を参考に、"Hello world!" と出力するプログラムを生成するプログラム

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

class Hoge {
	public static void Main(String[] args) {
		String filename = "test";

		AppDomain ad = Thread.GetDomain(); //AppDomain.CreateDomain("First", null, null);
		AssemblyName an = new AssemblyName();
		an.Name = filename + ".exe"; //AssemblyName.CreateSimpleName(filename + ".exe", "LispExe", "Lisp Executable", "default_alias");
		AssemblyBuilder ab = ad.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave);
		ModuleBuilder mb = ab.DefineDynamicModule(filename + ".exe", filename + ".exe");
		TypeBuilder tb = mb.DefineType(filename);
		MethodBuilder methodb = tb.DefineMethod("Main", MethodAttributes.Static | MethodAttributes.Public, typeof(void), null);
		ILGenerator il = methodb.GetILGenerator();

		String Name = "Hello world!";

		// コード生成
		Type[] type = new Type[1];
		type[0] = typeof(System.String);
		il.Emit(OpCodes.Ldstr, Name);
		il.Emit(OpCodes.Call, ((typeof(System.Console)).GetMethod("WriteLine", type)));

		il.Emit(OpCodes.Ret);
		tb.CreateType();

		ab.SetEntryPoint((mb.GetType(filename)).GetMethod("Main"));
		ab.Save(filename + ".exe");
	}
}
  • Ldstr で文字列をスタックに積む
  • Call で呼び出し
  • Ret でリターン
動的にプログラムを作成して実行

hello, world… LCG (Lightweight Code Gen) style! – Joel Pobar's CLR weblog

using System;
//using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

class Hoge {
	public static void Main(String[] args) {
		DynamicMethod dm = new DynamicMethod("HelloWorld", typeof(void), new Type[] {}, typeof(Hoge), false);
		ILGenerator il = dm.GetILGenerator();

		il.Emit(OpCodes.Ldstr, "hello, world");
		il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
		il.Emit(OpCodes.Ret);

		dm.Invoke(null, null);
	}
}
メソッドを生成して呼び出す

DynamicMethod.GetILGenerator Method (System.Reflection.Emit)

  • CreateDelegateとか
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

class Hoge {
	// Declare a delegate that will be used to execute the completed
	// dynamic method. 
	private delegate int FibInvoker(int ret);

	public static void Main(String[] args) {
		DynamicMethod fib = new DynamicMethod("fib", typeof(int), new Type[] {typeof(int)}, typeof(Hoge).Module);
		ILGenerator il = fib.GetILGenerator();

		Label FalseLabel = il.DefineLabel();

		il.Emit(OpCodes.Ldarg_0);
		emit_fixnum(il, 2);
		il.Emit(OpCodes.Bge, FalseLabel);		// if (n < 2)

		il.Emit(OpCodes.Ldarg_0);				//		return n
		il.Emit(OpCodes.Ret);

		il.MarkLabel(FalseLabel);				// else

		il.Emit(OpCodes.Ldarg_0);
		emit_fixnum(il, 1);
		il.Emit(OpCodes.Sub);
		il.Emit(OpCodes.Call, fib);				//		fib(n-1)

		il.Emit(OpCodes.Ldarg_0);
		emit_fixnum(il, 2);
		il.Emit(OpCodes.Sub);
		il.Emit(OpCodes.Call, fib);				//		fib(n-2)

		il.Emit(OpCodes.Add);					//		+
		il.Emit(OpCodes.Ret);

		// Create a delegate that represents the dynamic method. This
		// action completes the method, and any further attempts to
		// change the method will cause an exception.
		FibInvoker fi = (FibInvoker) fib.CreateDelegate(typeof(FibInvoker));

		for (int i=0; i<=35; ++i) {
			int res = fi(i);
			Console.WriteLine(i.ToString() + ":" + res.ToString());
		}
	}

	public static void emit_fixnum(ILGenerator il, long v) {
		switch (v) {
		case 0:	il.Emit(OpCodes.Ldc_I4_0);	break;
		case 1:	il.Emit(OpCodes.Ldc_I4_1);	break;
		case 2:	il.Emit(OpCodes.Ldc_I4_2);	break;
		case 3:	il.Emit(OpCodes.Ldc_I4_3);	break;
		case 4:	il.Emit(OpCodes.Ldc_I4_4);	break;
		case 5:	il.Emit(OpCodes.Ldc_I4_5);	break;
		case 6:	il.Emit(OpCodes.Ldc_I4_6);	break;
		case 7:	il.Emit(OpCodes.Ldc_I4_7);	break;
		case 8:	il.Emit(OpCodes.Ldc_I4_8);	break;
		default:
			if (-128 < v && v < 127)
				il.Emit(OpCodes.Ldc_I4_S, (byte)v);
			else if (Int32.MinValue < v && v < Int32.MaxValue)
				il.Emit(OpCodes.Ldc_I4, (int)v);
			else
				il.Emit(OpCodes.Ldc_I8, v);
			break;
		}
	}
}
  • MSILのフィボナッチできた
    • Br で無条件ジャンプ, Bge などで条件ジャンプ
    • Ldarg_n で引数参照
    • Add とか Sub とか
    • Ldc_xx で整数値を積む
  • ちょっと違うな… typeof(Hoge).GetMethod("fib", new Type[] { typeof(int) }) で取得できないし…

LLVMはレジスタマシン、MSILはスタックマシン。MSILも値の型を指定する必要がある。

mokehehemokehehe 2010/05/04 22:50 DynamicMethodは .NET Framework SDK 2.0 じゃないと使えない?

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。

トラックバック - http://d.hatena.ne.jp/mokehehe/20100502/msil