Author: zoltan Date: 2008-02-13 09:58:33 -0500 (Wed, 13 Feb 2008) New Revision: 95582
Added:
trunk/mcs/class/System/System.Text.RegularExpressions/CILCompiler.cs
Modified:
trunk/mcs/class/System/System.Text.RegularExpressions/ChangeLog
trunk/mcs/class/System/System.Text.RegularExpressions/RxCompiler.cs
Log:
2008-02-13 Zoltan Varga <[EMAIL PROTECTED]>
* CILCompiler.cs: New file, extracted from RxCompiler.cs.
Added: trunk/mcs/class/System/System.Text.RegularExpressions/CILCompiler.cs
===================================================================
--- trunk/mcs/class/System/System.Text.RegularExpressions/CILCompiler.cs
2008-02-13 14:58:29 UTC (rev 95581)
+++ trunk/mcs/class/System/System.Text.RegularExpressions/CILCompiler.cs
2008-02-13 14:58:33 UTC (rev 95582)
@@ -0,0 +1,504 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace System.Text.RegularExpressions {
+
+ //
+ // Compiler which generates IL bytecode to perform the matching instead
of
+ // interpreting a program.
+ // For simplicity, we inherit from RxCompiler, and generate the IL code
based
+ // on the program generated by it. This also allows us to fallback to
interpretation
+ // if we can't handle something.
+ // This is net 2.0, since 1.0 doesn't support DynamicMethods
+ // FIXME: Add support for 1.0, and CompileToAssembly
+ // FIXME: Emit one method with a switch instead of many methods
+ // FIXME: Avoid ref ptr, since it is bad for register allocation
+ //
+
+#if NET_2_0
+ class CILCompiler : RxCompiler, ICompiler {
+ DynamicMethod[] eval_methods;
+ bool[] eval_methods_defined;
+
+ IMachineFactory ICompiler.GetMachineFactory () {
+ byte[] code = new byte [curpos];
+ Buffer.BlockCopy (program, 0, code, 0, curpos);
+
+ eval_methods = new DynamicMethod [code.Length];
+ eval_methods_defined = new bool [code.Length];
+
+ // The main eval method
+ DynamicMethod main = GetEvalMethod (code, 11);
+ if (main != null)
+ return new RxInterpreterFactory (code,
(EvalDelegate)main.CreateDelegate (typeof (EvalDelegate)));
+ else
+ return new RxInterpreterFactory (code, null);
+ }
+
+ DynamicMethod GetEvalMethod (byte[] program, int pc) {
+ if (eval_methods_defined [pc])
+ return eval_methods [pc];
+
+ // FIXME: Recursion ?
+ eval_methods_defined [pc] = true;
+
+ eval_methods [pc] = CreateEvalMethod (program, pc);
+ return eval_methods [pc];
+ }
+
+ private FieldInfo GetInterpreterField (string name) {
+ return typeof (RxInterpreter).GetField (name,
BindingFlags.Instance|BindingFlags.NonPublic);
+ }
+
+ private MethodInfo GetInterpreterMethod (string name) {
+ return typeof (RxInterpreter).GetMethod (name,
BindingFlags.Instance|BindingFlags.NonPublic);
+ }
+
+ private string GetString (ushort[] program, int pc) {
+ int len = program[pc + 1];
+ int str = pc + 2;
+
+ char[] cs = new char[len];
+ for (int i = 0; i < len; ++ i)
+ cs[i] = (char)program[str ++];
+
+ return new string (cs);
+ }
+
+ private int ReadInt (byte[] code, int pc)
+ {
+ int val = code [pc];
+ val |= code [pc + 1] << 8;
+ val |= code [pc + 2] << 16;
+ val |= code [pc + 3] << 24;
+ return val;
+ }
+
+ private MethodInfo GetEvalMethodWithFallback (byte[] program,
int pc, out bool fallback) {
+ DynamicMethod eval = GetEvalMethod (program, pc);
+ fallback = eval != null;
+ if (eval != null) {
+ fallback = false;
+ return eval;
+ } else {
+ fallback = true;
+ return GetInterpreterMethod ("EvalByteCode");
+ }
+ }
+
+ //
+ // Create a dynamic method which is the Interpreter.Eval method
specialized
+ // to the given program and a given pc.
+ // Keep this in synch with Interpreter.Eval ()
+ //
+ DynamicMethod CreateEvalMethod (byte[] program, int pc) {
+ DynamicMethod m = new DynamicMethod ("Eval_" + pc,
typeof (bool), new Type [] { typeof (RxInterpreter), typeof (int), typeof
(int).MakeByRefType () }, typeof (RxInterpreter), true);
+ ILGenerator ilgen = m.GetILGenerator ();
+
+ /*
+ Args:
+ interp - 0
+ strpos - 1
+ strpos_result - 2
+ */
+
+ int start, length, end;
+ bool fallback;
+
+ Label label_fail = ilgen.DefineLabel ();
+
+ while (true) {
+ RxOp op = (RxOp)program [pc];
+
+ switch (op) {
+ case RxOp.Anchor: {
+ length = program [pc + 3] | (program
[pc + 4] << 8);
+ pc += program [pc + 1] | (program [pc +
2] << 8);
+
+ // FIXME: Group support
+
+ // Optimize some common cases by
inlining the code generated for the
+ // anchor body
+ RxOp anch_op = (RxOp)program [pc];
+ // FIXME: Do this even if the archor op
is not the last in the regex
+ if (anch_op == RxOp.Char &&
(RxOp)program [pc + 2] == RxOp.True) {
+
+ /*
+ * while (strpos < string_end) {
+ * if (str [strpos] ==
program [pc + 1]) {
+ * match_start = strpos;
+ * strpos_result = strpos +
1;
+ * return true;
+ * }
+ * strpos ++;
+ * }
+ * return false;
+ */
+ // Add some locals to avoid an
indirection
+ LocalBuilder local_string_end =
ilgen.DeclareLocal (typeof (int));
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("string_end"));
+ ilgen.Emit (OpCodes.Stloc,
local_string_end);
+ LocalBuilder local_str =
ilgen.DeclareLocal (typeof (string));
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("str"));
+ ilgen.Emit (OpCodes.Stloc,
local_str);
+
+ //while (strpos < string_end) {
+ // -> Done at the end of the
loop like mcs does
+ Label l1 = ilgen.DefineLabel ();
+ Label l2 = ilgen.DefineLabel ();
+ ilgen.Emit (OpCodes.Br, l2);
+ ilgen.MarkLabel (l1);
+
+ // if (str [strpos] == program
[pc + 1]) {
+ Label l3 = ilgen.DefineLabel ();
+ ilgen.Emit (OpCodes.Ldloc,
local_str);
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Callvirt,
typeof (string).GetMethod ("get_Chars"));
+ ilgen.Emit (OpCodes.Conv_I4);
+ ilgen.Emit (OpCodes.Ldc_I4,
(int)program [pc + 1]);
+ ilgen.Emit (OpCodes.Bne_Un, l3);
+
+ // match_start = strpos;
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Stfld,
GetInterpreterField ("match_start"));
+ // strpos_result = strpos +
1;
+ ilgen.Emit (OpCodes.Ldarg_2);
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Stind_I4);
+ // return true;
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Ret);
+ // }
+ ilgen.MarkLabel (l3);
+ // strpos++;
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Starg, 1);
+ //}
+ ilgen.MarkLabel (l2);
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldloc,
local_string_end);
+ ilgen.Emit (OpCodes.Blt, l1);
+
+ //return false;
+ ilgen.Emit (OpCodes.Br,
label_fail);
+ } else {
+ // General case
+
+ //Console.WriteLine ("Anchor op
" + anch_op);
+
+ //while (strpos < string_end) {
+ // -> Done at the end of the
loop like mcs does
+ Label l1 = ilgen.DefineLabel ();
+ Label l2 = ilgen.DefineLabel ();
+ ilgen.Emit (OpCodes.Br, l2);
+ ilgen.MarkLabel (l1);
+ // int res = strpos;
+ LocalBuilder local_res =
ilgen.DeclareLocal (typeof (int));
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Stloc,
local_res);
+ // if (EvalByteCode (pc,
strpos, ref res)) {
+ Label l3 = ilgen.DefineLabel ();
+ MethodInfo eval =
GetEvalMethodWithFallback (program, pc, out fallback);
+ ilgen.Emit (OpCodes.Ldarg_0);
+ if (fallback)
+ ilgen.Emit
(OpCodes.Ldc_I4, pc);
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldloca,
local_res);
+ ilgen.Emit (OpCodes.Call, eval);
+ ilgen.Emit (OpCodes.Brfalse,
l3);
+ // match_start = strpos;
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Stfld,
GetInterpreterField ("match_start"));
+ // strpos_result = res;
+ ilgen.Emit (OpCodes.Ldarg_2);
+ ilgen.Emit (OpCodes.Ldloc,
local_res);
+ ilgen.Emit (OpCodes.Stind_I4);
+ // return true;
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Ret);
+ // }
+ ilgen.MarkLabel (l3);
+ // strpos++;
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Starg, 1);
+ //}
+ ilgen.MarkLabel (l2);
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("string_end"));
+ ilgen.Emit (OpCodes.Blt, l1);
+ //return false;
+ ilgen.Emit (OpCodes.Ldc_I4_0);
+ ilgen.Emit (OpCodes.Ret);
+ }
+
+ goto End;
+ }
+ case RxOp.Branch: {
+ //int res = 0;
+ LocalBuilder local_res =
ilgen.DeclareLocal (typeof (int));
+ // FIXME: Emit a branch around the rest
of the code instead
+ //if (EvalByteCode (pc + 3, strpos, ref
res)) {
+ Label l1 = ilgen.DefineLabel ();
+ MethodInfo eval =
GetEvalMethodWithFallback (program, pc + 3, out fallback);
+ ilgen.Emit (OpCodes.Ldarg_0);
+ if (fallback)
+ ilgen.Emit (OpCodes.Ldc_I4, pc
+ 3);
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldloca, local_res);
+ ilgen.Emit (OpCodes.Call, eval);
+ ilgen.Emit (OpCodes.Brfalse, l1);
+ // strpos_result = res;
+ ilgen.Emit (OpCodes.Ldarg_2);
+ ilgen.Emit (OpCodes.Ldloc, local_res);
+ ilgen.Emit (OpCodes.Stind_I4);
+ // return true;
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Ret);
+ //}
+ ilgen.MarkLabel (l1);
+
+ pc += program [pc + 1] | (program [pc +
2] << 8);
+ break;
+ }
+ case RxOp.Char: {
+ //if (strpos < string_end && (str
[strpos] == program [pc + 1])) {
+ Label l1 = ilgen.DefineLabel ();
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("string_end"));
+ ilgen.Emit (OpCodes.Bge, l1);
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("str"));
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Callvirt, typeof
(string).GetMethod ("get_Chars"));
+ ilgen.Emit (OpCodes.Conv_I4);
+ ilgen.Emit (OpCodes.Ldc_I4,
(int)program [pc + 1]);
+ ilgen.Emit (OpCodes.Bne_Un, l1);
+ // strpos++;
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Starg, 1);
+ Label l2 = ilgen.DefineLabel ();
+ ilgen.Emit (OpCodes.Br, l2);
+ //}
+ ilgen.MarkLabel (l1);
+ //return false;
+ ilgen.Emit (OpCodes.Ldc_I4_0);
+ ilgen.Emit (OpCodes.Ret);
+ ilgen.MarkLabel (l2);
+
+ pc += 2;
+ break;
+ }
+ case RxOp.True: {
+ //strpos_result = strpos;
+ ilgen.Emit (OpCodes.Ldarg_2);
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Stind_I4);
+ // return true;
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Ret);
+ goto End;
+ }
+ case RxOp.False: {
+ // return false;
+ ilgen.Emit (OpCodes.Ldc_I4_0);
+ ilgen.Emit (OpCodes.Ret);
+ goto End;
+ }
+ case RxOp.Bitmap: {
+ //if (strpos < string_end) {
+ Label l1 = ilgen.DefineLabel ();
+ Label l2 = ilgen.DefineLabel ();
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("string_end"));
+ ilgen.Emit (OpCodes.Bge, l1);
+ // int c = str [strpos];
+ LocalBuilder local_c =
ilgen.DeclareLocal (typeof (int));
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("str"));
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Callvirt, typeof
(string).GetMethod ("get_Chars"));
+ ilgen.Emit (OpCodes.Conv_I4);
+ // c -= program [pc + 1];
+ ilgen.Emit (OpCodes.Ldc_I4,
(int)program [pc + 1]);
+ ilgen.Emit (OpCodes.Sub);
+ ilgen.Emit (OpCodes.Stloc, local_c);
+ length = program [pc + 2];
+ // if (c < 0 || c >= (length << 3))
+ // return false;
+ ilgen.Emit (OpCodes.Ldloc, local_c);
+ ilgen.Emit (OpCodes.Ldc_I4_0);
+ ilgen.Emit (OpCodes.Blt, label_fail);
+ ilgen.Emit (OpCodes.Ldloc, local_c);
+ ilgen.Emit (OpCodes.Ldc_I4, length <<
3);
+ ilgen.Emit (OpCodes.Bge, label_fail);
+ pc += 3;
+
+ // Optimized version for small bitmaps
+ if (length <= 4) {
+ uint bitmap = program [pc];
+
+ if (length > 1)
+ bitmap |=
((uint)program [pc + 1] << 8);
+ if (length > 2)
+ bitmap |=
((uint)program [pc + 2] << 16);
+ if (length > 3)
+ bitmap |=
((uint)program [pc + 3] << 24);
+
+ //if ((bitmap >> c) & 1)
+ ilgen.Emit (OpCodes.Ldc_I4,
bitmap);
+ ilgen.Emit (OpCodes.Ldloc,
local_c);
+ ilgen.Emit (OpCodes.Shr_Un);
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.And);
+ ilgen.Emit (OpCodes.Brfalse,
l1);
+ } else {
+ // if ((program [pc + (c >>
3)] & (1 << (c & 0x7))) != 0) {
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("program"));
+ ilgen.Emit (OpCodes.Ldloc,
local_c);
+ ilgen.Emit (OpCodes.Ldc_I4_3);
+ ilgen.Emit (OpCodes.Shr);
+ ilgen.Emit (OpCodes.Ldc_I4, pc);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Ldelem_I1);
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Ldloc,
local_c);
+ ilgen.Emit (OpCodes.Ldc_I4, 7);
+ ilgen.Emit (OpCodes.And);
+ ilgen.Emit (OpCodes.Shl);
+ ilgen.Emit (OpCodes.And);
+ ilgen.Emit (OpCodes.Ldc_I4_0);
+ ilgen.Emit (OpCodes.Beq, l1);
+ }
+ // strpos++;
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Starg, 1);
+ // continue;
+ ilgen.Emit (OpCodes.Br, l2);
+ // }
+ //}
+ //return false;
+ ilgen.MarkLabel (l1);
+ ilgen.Emit (OpCodes.Ldc_I4_0);
+ ilgen.Emit (OpCodes.Ret);
+
+ ilgen.MarkLabel (l2);
+
+ pc += length;
+ break;
+ }
+ case RxOp.String: {
+ start = pc + 2;
+ length = program [pc + 1];
+ //if (strpos + length > string_end)
+ // return false;
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldc_I4, length);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("string_end"));
+ ilgen.Emit (OpCodes.Bgt, label_fail);
+
+ /* Avoid unsafe code in Moonlight build
*/
+#if false && !NET_2_1
+ LocalBuilder local_strptr =
ilgen.DeclareLocal (typeof (char).MakePointerType ());
+ // char *strptr = &str.start_char +
strpos
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("str"));
+ ilgen.Emit (OpCodes.Ldflda, typeof
(String).GetField ("start_char", BindingFlags.Instance|BindingFlags.NonPublic));
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Shl);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Stloc,
local_strptr);
+
+ end = start + length;
+ for (; start < end; ++start) {
+ // if (*(strptr + start) !=
program [start])
+ // return false;
+ ilgen.Emit (OpCodes.Ldloc,
local_strptr);
+ ilgen.Emit (OpCodes.Ldc_I4,
start * 2);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Ldind_I2);
+ ilgen.Emit (OpCodes.Ldc_I4,
(int)program [start]);
+ ilgen.Emit (OpCodes.Bne_Un,
label_fail);
+ }
+
+ // strpos += length
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldc_I4, length);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Starg, 1);
+
+#else
+ // Allocate a local for 'str' to save
an indirection
+ LocalBuilder local_str =
ilgen.DeclareLocal (typeof (string));
+ ilgen.Emit (OpCodes.Ldarg_0);
+ ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("str"));
+ ilgen.Emit (OpCodes.Stloc, local_str);
+
+ // FIXME: Emit a loop for long strings
+ end = start + length;
+ for (; start < end; ++start) {
+ //if (str [strpos] != program
[start])
+ // return false;
+ ilgen.Emit (OpCodes.Ldloc,
local_str);
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Callvirt,
typeof (string).GetMethod ("get_Chars"));
+ ilgen.Emit (OpCodes.Ldc_I4,
(int)program [start]);
+ ilgen.Emit (OpCodes.Bne_Un,
label_fail);
+ //strpos++;
+ ilgen.Emit (OpCodes.Ldarg_1);
+ ilgen.Emit (OpCodes.Ldc_I4_1);
+ ilgen.Emit (OpCodes.Add);
+ ilgen.Emit (OpCodes.Starg, 1);
+ }
+#endif
+
+ pc = end;
+ break;
+ }
+ case RxOp.Jump:
+ pc += program [pc + 1] | (program [pc +
2] << 8);
+ continue;
+ default:
+ Console.WriteLine ("Opcode " + op + "
not supported.");
+ return null;
+ }
+ }
+
+ End:
+ ilgen.MarkLabel (label_fail);
+ ilgen.Emit (OpCodes.Ldc_I4_0);
+ ilgen.Emit (OpCodes.Ret);
+
+ return m;
+ }
+ }
+#else
+ class CILCompiler : RxCompiler {
+ }
+#endif
+
+}
+
Modified: trunk/mcs/class/System/System.Text.RegularExpressions/ChangeLog
===================================================================
--- trunk/mcs/class/System/System.Text.RegularExpressions/ChangeLog
2008-02-13 14:58:29 UTC (rev 95581)
+++ trunk/mcs/class/System/System.Text.RegularExpressions/ChangeLog
2008-02-13 14:58:33 UTC (rev 95582)
@@ -1,5 +1,7 @@
2008-02-13 Zoltan Varga <[EMAIL PROTECTED]>
+ * CILCompiler.cs: New file, extracted from RxCompiler.cs.
+
* RxInterpreter.cs RxCompiler.cs: First version of regex IL compiler.
Wed Feb 13 13:18:12 CET 2008 Paolo Molaro <[EMAIL PROTECTED]>
Modified: trunk/mcs/class/System/System.Text.RegularExpressions/RxCompiler.cs
===================================================================
--- trunk/mcs/class/System/System.Text.RegularExpressions/RxCompiler.cs
2008-02-13 14:58:29 UTC (rev 95581)
+++ trunk/mcs/class/System/System.Text.RegularExpressions/RxCompiler.cs
2008-02-13 14:58:33 UTC (rev 95582)
@@ -688,499 +688,6 @@
}
- //
- // Compiler which generates IL bytecode to perform the matching instead
of
- // interpreting a program.
- // For simplicity, we inherit from RxCompiler, and generate the IL code
based
- // on the program generated by it. This also allows us to fallback to
interpretation
- // if we can't handle something.
- // This is net 2.0, since 1.0 doesn't support DynamicMethods
- // FIXME: Add support for 1.0, and CompileToAssembly
- // FIXME: Emit one method with a switch instead of many methods
- // FIXME: Avoid ref ptr, since it is bad for register allocation
- //
-
-#if NET_2_0
- class CILCompiler : RxCompiler, ICompiler {
- DynamicMethod[] eval_methods;
- bool[] eval_methods_defined;
-
- IMachineFactory ICompiler.GetMachineFactory () {
- byte[] code = new byte [curpos];
- Buffer.BlockCopy (program, 0, code, 0, curpos);
-
- eval_methods = new DynamicMethod [code.Length];
- eval_methods_defined = new bool [code.Length];
-
- // The main eval method
- DynamicMethod main = GetEvalMethod (code, 11);
- if (main != null)
- return new RxInterpreterFactory (code,
(EvalDelegate)main.CreateDelegate (typeof (EvalDelegate)));
- else
- return new RxInterpreterFactory (code, null);
- }
-
- DynamicMethod GetEvalMethod (byte[] program, int pc) {
- if (eval_methods_defined [pc])
- return eval_methods [pc];
-
- // FIXME: Recursion ?
- eval_methods_defined [pc] = true;
-
- eval_methods [pc] = CreateEvalMethod (program, pc);
- return eval_methods [pc];
- }
-
- private FieldInfo GetInterpreterField (string name) {
- return typeof (RxInterpreter).GetField (name,
BindingFlags.Instance|BindingFlags.NonPublic);
- }
-
- private MethodInfo GetInterpreterMethod (string name) {
- return typeof (RxInterpreter).GetMethod (name,
BindingFlags.Instance|BindingFlags.NonPublic);
- }
-
- private string GetString (ushort[] program, int pc) {
- int len = program[pc + 1];
- int str = pc + 2;
-
- char[] cs = new char[len];
- for (int i = 0; i < len; ++ i)
- cs[i] = (char)program[str ++];
-
- return new string (cs);
- }
-
- private int ReadInt (byte[] code, int pc)
- {
- int val = code [pc];
- val |= code [pc + 1] << 8;
- val |= code [pc + 2] << 16;
- val |= code [pc + 3] << 24;
- return val;
- }
-
- private MethodInfo GetEvalMethodWithFallback (byte[] program,
int pc, out bool fallback) {
- DynamicMethod eval = GetEvalMethod (program, pc);
- fallback = eval != null;
- if (eval != null) {
- fallback = false;
- return eval;
- } else {
- fallback = true;
- return GetInterpreterMethod ("EvalByteCode");
- }
- }
-
- //
- // Create a dynamic method which is the Interpreter.Eval method
specialized
- // to the given program and a given pc.
- // Keep this in synch with Interpreter.Eval ()
- //
- DynamicMethod CreateEvalMethod (byte[] program, int pc) {
- DynamicMethod m = new DynamicMethod ("Eval_" + pc,
typeof (bool), new Type [] { typeof (RxInterpreter), typeof (int), typeof
(int).MakeByRefType () }, typeof (RxInterpreter), true);
- ILGenerator ilgen = m.GetILGenerator ();
-
- /*
- Args:
- interp - 0
- strpos - 1
- strpos_result - 2
- */
-
- int start, length, end;
- bool fallback;
-
- Label label_fail = ilgen.DefineLabel ();
-
- while (true) {
- RxOp op = (RxOp)program [pc];
-
- switch (op) {
- case RxOp.Anchor: {
- length = program [pc + 3] | (program
[pc + 4] << 8);
- pc += program [pc + 1] | (program [pc +
2] << 8);
-
- // FIXME: Group support
-
- // Optimize some common cases by
inlining the code generated for the
- // anchor body
- RxOp anch_op = (RxOp)program [pc];
- // FIXME: Do this even if the archor op
is not the last in the regex
- if (anch_op == RxOp.Char &&
(RxOp)program [pc + 2] == RxOp.True) {
-
- /*
- * while (strpos < string_end) {
- * if (str [strpos] ==
program [pc + 1]) {
- * match_start = strpos;
- * strpos_result = strpos +
1;
- * return true;
- * }
- * strpos ++;
- * }
- * return false;
- */
- // Add some locals to avoid an
indirection
- LocalBuilder local_string_end =
ilgen.DeclareLocal (typeof (int));
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("string_end"));
- ilgen.Emit (OpCodes.Stloc,
local_string_end);
- LocalBuilder local_str =
ilgen.DeclareLocal (typeof (string));
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("str"));
- ilgen.Emit (OpCodes.Stloc,
local_str);
-
- //while (strpos < string_end) {
- // -> Done at the end of the
loop like mcs does
- Label l1 = ilgen.DefineLabel ();
- Label l2 = ilgen.DefineLabel ();
- ilgen.Emit (OpCodes.Br, l2);
- ilgen.MarkLabel (l1);
-
- // if (str [strpos] == program
[pc + 1]) {
- Label l3 = ilgen.DefineLabel ();
- ilgen.Emit (OpCodes.Ldloc,
local_str);
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Callvirt,
typeof (string).GetMethod ("get_Chars"));
- ilgen.Emit (OpCodes.Conv_I4);
- ilgen.Emit (OpCodes.Ldc_I4,
(int)program [pc + 1]);
- ilgen.Emit (OpCodes.Bne_Un, l3);
-
- // match_start = strpos;
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Stfld,
GetInterpreterField ("match_start"));
- // strpos_result = strpos +
1;
- ilgen.Emit (OpCodes.Ldarg_2);
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Stind_I4);
- // return true;
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Ret);
- // }
- ilgen.MarkLabel (l3);
- // strpos++;
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Starg, 1);
- //}
- ilgen.MarkLabel (l2);
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldloc,
local_string_end);
- ilgen.Emit (OpCodes.Blt, l1);
-
- //return false;
- ilgen.Emit (OpCodes.Br,
label_fail);
- } else {
- // General case
-
- //Console.WriteLine ("Anchor op
" + anch_op);
-
- //while (strpos < string_end) {
- Label l1 = ilgen.DefineLabel ();
- Label l2 = ilgen.DefineLabel ();
- ilgen.MarkLabel (l1);
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("string_end"));
- ilgen.Emit (OpCodes.Bge, l2);
- // int res = strpos;
- LocalBuilder local_res =
ilgen.DeclareLocal (typeof (int));
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Stloc,
local_res);
- // if (EvalByteCode (pc,
strpos, ref res)) {
- Label l3 = ilgen.DefineLabel ();
- MethodInfo eval =
GetEvalMethodWithFallback (program, pc, out fallback);
- ilgen.Emit (OpCodes.Ldarg_0);
- if (fallback)
- ilgen.Emit
(OpCodes.Ldc_I4, pc);
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldloca,
local_res);
- ilgen.Emit (OpCodes.Call, eval);
- ilgen.Emit (OpCodes.Brfalse,
l3);
- // match_start = strpos;
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Stfld,
GetInterpreterField ("match_start"));
- // strpos_result = res;
- ilgen.Emit (OpCodes.Ldarg_2);
- ilgen.Emit (OpCodes.Ldloc,
local_res);
- ilgen.Emit (OpCodes.Stind_I4);
- // return true;
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Ret);
- // }
- ilgen.MarkLabel (l3);
- // strpos++;
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Starg, 1);
- //}
- ilgen.Emit (OpCodes.Br, l1);
- ilgen.MarkLabel (l2);
- //return false;
- ilgen.Emit (OpCodes.Ldc_I4_0);
- ilgen.Emit (OpCodes.Ret);
- }
-
- goto End;
- }
- case RxOp.Branch: {
- //int res = 0;
- LocalBuilder local_res =
ilgen.DeclareLocal (typeof (int));
- // FIXME: Emit a branch around the rest
of the code instead
- //if (EvalByteCode (pc + 3, strpos, ref
res)) {
- Label l1 = ilgen.DefineLabel ();
- MethodInfo eval =
GetEvalMethodWithFallback (program, pc + 3, out fallback);
- ilgen.Emit (OpCodes.Ldarg_0);
- if (fallback)
- ilgen.Emit (OpCodes.Ldc_I4, pc
+ 3);
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldloca, local_res);
- ilgen.Emit (OpCodes.Call, eval);
- ilgen.Emit (OpCodes.Brfalse, l1);
- // strpos_result = res;
- ilgen.Emit (OpCodes.Ldarg_2);
- ilgen.Emit (OpCodes.Ldloc, local_res);
- ilgen.Emit (OpCodes.Stind_I4);
- // return true;
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Ret);
- //}
- ilgen.MarkLabel (l1);
-
- pc += program [pc + 1] | (program [pc +
2] << 8);
- break;
- }
- case RxOp.Char: {
- //if (strpos < string_end && (str
[strpos] == program [pc + 1])) {
- Label l1 = ilgen.DefineLabel ();
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("string_end"));
- ilgen.Emit (OpCodes.Bge, l1);
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("str"));
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Callvirt, typeof
(string).GetMethod ("get_Chars"));
- ilgen.Emit (OpCodes.Conv_I4);
- ilgen.Emit (OpCodes.Ldc_I4,
(int)program [pc + 1]);
- ilgen.Emit (OpCodes.Bne_Un, l1);
- // strpos++;
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Starg, 1);
- Label l2 = ilgen.DefineLabel ();
- ilgen.Emit (OpCodes.Br, l2);
- //}
- ilgen.MarkLabel (l1);
- //return false;
- ilgen.Emit (OpCodes.Ldc_I4_0);
- ilgen.Emit (OpCodes.Ret);
- ilgen.MarkLabel (l2);
-
- pc += 2;
- break;
- }
- case RxOp.True: {
- //strpos_result = strpos;
- ilgen.Emit (OpCodes.Ldarg_2);
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Stind_I4);
- // return true;
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Ret);
- goto End;
- }
- case RxOp.False: {
- // return false;
- ilgen.Emit (OpCodes.Ldc_I4_0);
- ilgen.Emit (OpCodes.Ret);
- goto End;
- }
- case RxOp.Bitmap: {
- //if (strpos < string_end) {
- Label l1 = ilgen.DefineLabel ();
- Label l2 = ilgen.DefineLabel ();
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("string_end"));
- ilgen.Emit (OpCodes.Bge, l1);
- // int c = str [strpos];
- LocalBuilder local_c =
ilgen.DeclareLocal (typeof (int));
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("str"));
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Callvirt, typeof
(string).GetMethod ("get_Chars"));
- ilgen.Emit (OpCodes.Conv_I4);
- // c -= program [pc + 1];
- ilgen.Emit (OpCodes.Ldc_I4,
(int)program [pc + 1]);
- ilgen.Emit (OpCodes.Sub);
- ilgen.Emit (OpCodes.Stloc, local_c);
- length = program [pc + 2];
- // if (c < 0 || c >= (length << 3))
- // return false;
- ilgen.Emit (OpCodes.Ldloc, local_c);
- ilgen.Emit (OpCodes.Ldc_I4_0);
- ilgen.Emit (OpCodes.Blt, label_fail);
- ilgen.Emit (OpCodes.Ldloc, local_c);
- ilgen.Emit (OpCodes.Ldc_I4, length <<
3);
- ilgen.Emit (OpCodes.Bge, label_fail);
- pc += 3;
-
- // Optimized version for small bitmaps
- if (length <= 4) {
- uint bitmap = program [pc];
-
- if (length > 1)
- bitmap |=
((uint)program [pc + 1] << 8);
- if (length > 2)
- bitmap |=
((uint)program [pc + 2] << 16);
- if (length > 3)
- bitmap |=
((uint)program [pc + 3] << 24);
-
- //if ((bitmap >> c) & 1)
- ilgen.Emit (OpCodes.Ldc_I4,
bitmap);
- ilgen.Emit (OpCodes.Ldloc,
local_c);
- ilgen.Emit (OpCodes.Shr_Un);
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.And);
- ilgen.Emit (OpCodes.Brfalse,
l1);
- } else {
- // if ((program [pc + (c >>
3)] & (1 << (c & 0x7))) != 0) {
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("program"));
- ilgen.Emit (OpCodes.Ldloc,
local_c);
- ilgen.Emit (OpCodes.Ldc_I4_3);
- ilgen.Emit (OpCodes.Shr);
- ilgen.Emit (OpCodes.Ldc_I4, pc);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Ldelem_I1);
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Ldloc,
local_c);
- ilgen.Emit (OpCodes.Ldc_I4, 7);
- ilgen.Emit (OpCodes.And);
- ilgen.Emit (OpCodes.Shl);
- ilgen.Emit (OpCodes.And);
- ilgen.Emit (OpCodes.Ldc_I4_0);
- ilgen.Emit (OpCodes.Beq, l1);
- }
- // strpos++;
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Starg, 1);
- // continue;
- ilgen.Emit (OpCodes.Br, l2);
- // }
- //}
- //return false;
- ilgen.MarkLabel (l1);
- ilgen.Emit (OpCodes.Ldc_I4_0);
- ilgen.Emit (OpCodes.Ret);
-
- ilgen.MarkLabel (l2);
-
- pc += length;
- break;
- }
- case RxOp.String: {
- start = pc + 2;
- length = program [pc + 1];
- //if (strpos + length > string_end)
- // return false;
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldc_I4, length);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("string_end"));
- ilgen.Emit (OpCodes.Bgt, label_fail);
-
- /* Avoid unsafe code in Moonlight build
*/
-#if false && !NET_2_1
- LocalBuilder local_strptr =
ilgen.DeclareLocal (typeof (char).MakePointerType ());
- // char *strptr = &str.start_char +
strpos
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("str"));
- ilgen.Emit (OpCodes.Ldflda, typeof
(String).GetField ("start_char", BindingFlags.Instance|BindingFlags.NonPublic));
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Shl);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Stloc,
local_strptr);
-
- end = start + length;
- for (; start < end; ++start) {
- // if (*(strptr + start) !=
program [start])
- // return false;
- ilgen.Emit (OpCodes.Ldloc,
local_strptr);
- ilgen.Emit (OpCodes.Ldc_I4,
start * 2);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Ldind_I2);
- ilgen.Emit (OpCodes.Ldc_I4,
(int)program [start]);
- ilgen.Emit (OpCodes.Bne_Un,
label_fail);
- }
-
- // strpos += length
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldc_I4, length);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Starg, 1);
-
-#else
- // Allocate a local for 'str' to save
an indirection
- LocalBuilder local_str =
ilgen.DeclareLocal (typeof (string));
- ilgen.Emit (OpCodes.Ldarg_0);
- ilgen.Emit (OpCodes.Ldfld,
GetInterpreterField ("str"));
- ilgen.Emit (OpCodes.Stloc, local_str);
-
- // FIXME: Emit a loop for long strings
- end = start + length;
- for (; start < end; ++start) {
- //if (str [strpos] != program
[start])
- // return false;
- ilgen.Emit (OpCodes.Ldloc,
local_str);
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Callvirt,
typeof (string).GetMethod ("get_Chars"));
- ilgen.Emit (OpCodes.Ldc_I4,
(int)program [start]);
- ilgen.Emit (OpCodes.Bne_Un,
label_fail);
- //strpos++;
- ilgen.Emit (OpCodes.Ldarg_1);
- ilgen.Emit (OpCodes.Ldc_I4_1);
- ilgen.Emit (OpCodes.Add);
- ilgen.Emit (OpCodes.Starg, 1);
- }
-#endif
-
- pc = end;
- break;
- }
- case RxOp.Jump:
- pc += program [pc + 1] | (program [pc +
2] << 8);
- continue;
- default:
- Console.WriteLine ("Opcode " + op + "
not supported.");
- return null;
- }
- }
-
- End:
- ilgen.MarkLabel (label_fail);
- ilgen.Emit (OpCodes.Ldc_I4_0);
- ilgen.Emit (OpCodes.Ret);
-
- return m;
- }
- }
-#else
- class CILCompiler : RxCompiler {
- }
-#endif
-
class RxInterpreterFactory : IMachineFactory {
public RxInterpreterFactory (byte[] program, EvalDelegate
eval_del) {
this.program = program;
_______________________________________________
Mono-patches maillist - [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches
