private static EmitSyntax EmitAst(EmitSyntax emit, AstNode ast, Ref<Args> input) { var settings = new ILCompilerSettings { LdInput = il => il.Ldarg(input), SUCCESS = emit.Labels.Generate(), FAILURE = emit.Labels.Generate(), }; var _ = new ILNfaCompiler(ast, emit, settings); emit .Label(settings.SUCCESS) .Ldc_I4_1() .Ret() .Label(settings.FAILURE) .Ldc_I4_0() .Ret(); return emit; }
public ILNfaCompiler(AstNode node, EmitSyntax emit, ILCompilerSettings settings) { this.Scanner = new StemScanner(); this.emit = emit; this.settings = settings; var labels = emit.Labels; NEXT_THREAD = labels.Generate(); RESUME_ALL = labels.Generate(); NEXT_INPUT = labels.Generate(); THREAD_SUSPEND = labels.Generate(); THREAD_DISPATCH = labels.Generate(); var LABEL0 = labels.Generate(); var LABEL1 = labels.Generate(); var POSTCOMPILEINIT = labels.Generate(); var POSTCOMPILEINIT_BACK = labels.Generate(); var locals = emit.Locals; slots = locals.Generate(); labelIndexToLocation = locals.Generate(); intTmp = locals.Generate(); threadTmp = locals.Generate(); INfaVM code = this; this.inputPump = new ArrayLoopGenerator( valueType: emit.Types.Int32, ldarray: settings.LdInput, body: il => il .Do(matchedStack.Clear) .Br(RESUME_ALL.GetRef()) ); this.runningStack = new StackGenerator(emit, typeof(Thread)); this.suspendedStack = new StackGenerator(emit, typeof(Thread)); this.matchedStack = new StackGenerator(emit, typeof(Thread)); this.tmpStack = new StackGenerator(emit, typeof(Thread), nullContainer: true); emit .Local(intTmp, emit.Types.Int32) .Local(slots, emit.Types.Import(typeof(int[]))) .Local(labelIndexToLocation, emit.Types.Import(typeof(int[]))) .Br(POSTCOMPILEINIT.GetRef()) .Label(POSTCOMPILEINIT_BACK) .Local(threadTmp, emit.Types.Import(typeof(Thread))) .Ldloca(threadTmp.GetRef()) .Initobj(emit.Types.Import(typeof(Thread))) .Do(inputPump.EmitInitialization) ; new RegularNfaVMCompiler().Compile(node, code); int LabelCount = Math.Max(1, indexToLabel.Count); this.runningStack.SetSize(LabelCount); this.suspendedStack.SetSize(LabelCount); this.matchedStack.SetSize(LabelCount); this.tmpStack.SetSize(LabelCount); emit .Label(POSTCOMPILEINIT) .Do(runningStack.Init) .Do(suspendedStack.Init) .Do(matchedStack.Init) .Do(tmpStack.Init) .Ldc_I4(slotCount) .Newarr(emit.Types.Int32) .Stloc(slots.GetRef()) .Ldc_I4(LabelCount) .Newarr(emit.Types.Int32) .Stloc(labelIndexToLocation.GetRef()) // Fill labelIndexToLocation with -1 : .Ldc_I4(LabelCount).Stloc(intTmp.GetRef()) .Label(LABEL1) .Ldloc(intTmp.GetRef()) .Ldc_I4_0() .Beq(LABEL0.GetRef()) .Ldloc(intTmp.GetRef()) .Ldc_I4(1) .Sub() .Stloc(intTmp.GetRef()) .Ldloc(labelIndexToLocation.GetRef()) .Ldloc(intTmp.GetRef()) .Ldc_I4(int.MinValue) .Stelem_I4() .Br(LABEL1.GetRef()) .Label(LABEL0) .Br(POSTCOMPILEINIT_BACK.GetRef()); // Save thread as suspended (stack should already contain label index to suspend) emit.Label(THREAD_SUSPEND); StThreadValueByRuntimeLabelIndex(); emit // Don't add thread if same thread (same label index // with current input location) already exists in a list. .Ldloc(labelIndexToLocation.GetRef()) .Ldloca(threadTmp.GetRef()) .Ldfld(LabelIndexField) .Ldelem_I4() .Ldloc(inputPump.Index.GetRef()) .Beq(NEXT_THREAD.GetRef()) // Mark label index as visited .Ldloc(labelIndexToLocation.GetRef()) .Ldloca(threadTmp.GetRef()) .Ldfld(LabelIndexField) .Ldloc(inputPump.Index.GetRef()) .Stelem_I4() ; suspendedStack.PushFrom(emit, threadTmp); emit .Br(NEXT_THREAD.GetRef()) .Label(RESUME_ALL) .Swap(suspendedStack.Stack, runningStack.Stack, tmpStack.Stack) .Swap(suspendedStack.Index, runningStack.Index, tmpStack.Index) .Label(NEXT_THREAD) ; runningStack .StackLoop( emit, (emit2, thread) => { emit2 .Ldloca(thread.GetRef()) .Ldfld(LabelIndexField) .Label(THREAD_DISPATCH) .Switch(indexToLabel.Select(def => def.GetRef()).ToArray()) .Br(settings.FAILURE.GetRef()); }) ; emit .Br(NEXT_INPUT.GetRef()) ; emit .Label(NEXT_INPUT) ; inputPump.EmitLoopPass(emit, false); emit // Check if there are matched threads: .Do(matchedStack.LdCount) .Ldc_I4_0() .Beq(settings.FAILURE.GetRef()) .Br(settings.SUCCESS.GetRef()); }