internal CompiledProgram(IReadonlyInternalsMap internals, JitLine[] lines) { if (lines.Length < 1) { throw new ArgumentException("Cannot create a program with no lines", nameof(lines)); } InternalsMap = internals; _lines = lines; ProgramCounter = 1; }
public CompiledResult( int programCounter, TimeSpan compile, TimeSpan execute, IReadonlyInternalsMap internalsMap, IReadOnlyList <Value> internals, ExternalsMap externalsMap, IReadOnlyList <Value> externals) { _internalsMap = internalsMap; _internals = internals; _externalsMap = externalsMap; _externals = externals; Prepare = compile; Execute = execute; ProgramCounter = programCounter; }
public ArraySegmentMemoryAccessor( OptimisingEmitter <TEmit> emitter, ushort externalArraySegmentArg, ushort internalArraySegmentArg, IReadonlyInternalsMap internals, IReadonlyExternalsMap externals, IStaticTypeTracker types, Local?changeSet) { _emitter = emitter; _externalArraySegmentArg = externalArraySegmentArg; _internalArraySegmentArg = internalArraySegmentArg; _internals = internals; _externals = externals; _types = types; _changeSet = changeSet; _cache = new Dictionary <VariableName, TypedLocal>(); _cacheDirty = new Dictionary <VariableName, Local>(); _mutated = new HashSet <VariableName>(); }
/// <summary> /// Compile a line of Yolol into a runnable C# function /// </summary> /// <param name="line">The line of code to convert</param> /// <param name="lineNumber">The number of this line</param> /// <param name="maxLines">The max number of lines in a valid program (20, in standard Yolol)</param> /// <param name="maxStringLength"></param> /// <param name="internalVariableMap">A dictionary used for mapping variables to integers in all lines in this script</param> /// <param name="externalVariableMap">A dictionary used for mapping externals to integers in all lines in this script</param> /// <param name="staticTypes">Statically known types for variables</param> /// <param name="changeDetection"></param> /// <returns>A function which runs this line of code. Accepts two sections of memory, internal variables and external variables. Returns the line number to go to next</returns> public static Func <ArraySegment <Value>, ArraySegment <Value>, LineResult> Compile( this Line line, int lineNumber, int maxLines, int maxStringLength, IReadonlyInternalsMap internalVariableMap, IReadonlyExternalsMap externalVariableMap, IReadOnlyDictionary <VariableName, Type>?staticTypes = null, bool changeDetection = false ) { // Create an emitter for a dynamic method var emitter = Emit <Func <ArraySegment <Value>, ArraySegment <Value>, LineResult> > .NewDynamicMethod(strictBranchVerification : true); // Compile code into the emitter line.Compile(emitter, lineNumber, maxLines, maxStringLength, internalVariableMap, externalVariableMap, staticTypes, changeDetection); // Finally convert the IL into a runnable C# method for this line var del = emitter.CreateDelegate(); return(del); }
/// <summary> /// Compile a line of Yolol into the given IL emitter /// </summary> /// <param name="line"></param> /// <param name="emit"></param> /// <param name="lineNumber"></param> /// <param name="maxLines"></param> /// <param name="maxStringLength"></param> /// <param name="internalVariableMap"></param> /// <param name="externalVariableMap"></param> /// <param name="staticTypes"></param> /// <param name="changeDetection"></param> public static void Compile( this Line line, Emit <Func <ArraySegment <Value>, ArraySegment <Value>, LineResult> > emit, int lineNumber, int maxLines, int maxStringLength, IReadonlyInternalsMap internalVariableMap, IReadonlyExternalsMap externalVariableMap, IReadOnlyDictionary <VariableName, Type>?staticTypes = null, bool changeDetection = false ) { using (var emitter = new OptimisingEmitter <Func <ArraySegment <Value>, ArraySegment <Value>, LineResult> >(emit)) { void EmitFallthroughCalc() { if (lineNumber == maxLines) { emitter.LoadConstant(1); } else { emitter.LoadConstant(lineNumber + 1); } } // Special case for totally empty lines if (line.Statements.Statements.Count == 0) { EmitFallthroughCalc(); emitter.LoadConstant(0ul); emitter.NewObject <ChangeSet, ulong>(); emitter.NewObject(typeof(LineResult).GetConstructor(new[] { typeof(int), typeof(ChangeSet) }) !); emitter.Return(); return; } // Begin an exception block to catch Yolol runtime errors var exBlock = emitter.BeginExceptionBlock(); // Create a local to store the return address from inside the try/catch block var retAddr = emitter.DeclareLocal <int>("ret_addr", initializeReused: false); // Create a local for the change bit set var changeSet = changeDetection ? emitter.DeclareLocal <ulong>("change_set") : null; // Store the default return address to go to EmitFallthroughCalc(); emitter.StoreLocal(retAddr); // Create a label which any `goto` statements can use. They drop their destination PC on the stack and then jump to this label var gotoLabel = emitter.DefineLabel2("encountered_goto"); var types = new StaticTypeTracker(staticTypes); // Create a memory accessor which manages reading and writing the memory arrays using (var accessor = new ArraySegmentMemoryAccessor <Func <ArraySegment <Value>, ArraySegment <Value>, LineResult> >( emitter, 1, 0, internalVariableMap, externalVariableMap, types, changeSet )) { accessor.EmitLoad(line); // Convert the entire line into IL var converter = new ConvertLineVisitor <Func <ArraySegment <Value>, ArraySegment <Value>, LineResult> >(emitter, maxLines, accessor, exBlock, gotoLabel, types, maxStringLength); converter.Visit(line); // When a line finishes (with no gotos in the line) call flow eventually reaches here. Go to the next line. emitter.Leave(exBlock); // Create a block to handle gotos. The destination will already be on the stack, so just return if (gotoLabel.IsUsed) { emitter.MarkLabel(gotoLabel); emitter.StoreLocal(retAddr); emitter.Leave(exBlock); } // Catch all execution exceptions and return the appropriate next line number to fall through to var catchBlock = emitter.BeginCatchBlock <ExecutionException>(exBlock); #if DEBUG using (var ex = emitter.DeclareLocal(typeof(ExecutionException), initializeReused: false)) { emitter.StoreLocal(ex); emitter.WriteLine("execution exception: {0}", ex); } #else emitter.Pop(); #endif // Close the exception block which was wrapping the entire method emitter.EndCatchBlock(catchBlock); emitter.EndExceptionBlock(exBlock); } // Load the return address from inside the catch block emitter.LoadLocal(retAddr); // Create the change set, either from the computer value or the default value (which indicates that everything has changed) if (changeSet != null) { emitter.LoadLocal(changeSet); } else { emitter.LoadConstant(ulong.MaxValue); } emitter.NewObject <ChangeSet, ulong>(); emitter.NewObject(typeof(LineResult).GetConstructor(new[] { typeof(int), typeof(ChangeSet) }) !); emitter.Return(); emitter.Optimise(); #if DEBUG Console.WriteLine($"// {line}"); Console.WriteLine(emitter.ToString()); Console.WriteLine($"------------------------------"); #endif } }