private WasmFunctionDefinition MakeInterpreterThunk(int index, FunctionBody body, ILGenerator generator) { var signature = types[index]; // Create an interpreted function definition. var func = new WasmFunctionDefinition(signature, body, module); // Call it. EmitExternalCall( generator, signature.ParameterTypes.Count, func, signature.ParameterTypes .Select <WasmValueType, Func <ILGenerator, Type> >( (p, i) => gen => { gen.Emit(OpCodes.Ldarg, i); return(ValueHelpers.ToClrType(p)); }) .ToArray()); // Return. generator.Emit(OpCodes.Ret); return(func); }
/// <inheritdoc/> public override void Initialize(ModuleInstance module, int offset, IReadOnlyList <FunctionType> types) { this.module = module; this.offset = offset; this.types = types; this.helperFieldIndex = 0; this.constFieldValues = new Dictionary <FieldInfo, object>(); this.functionDefinitions = new List <CompiledFunctionDefinition>(); this.assembly = AssemblyBuilder.DefineDynamicAssembly( new AssemblyName("wasm"), AssemblyBuilderAccess.RunAndCollect); var wasmModule = assembly.DefineDynamicModule("main"); this.wasmType = wasmModule.DefineType("CompiledWasm", TypeAttributes.Public | TypeAttributes.Sealed); var builderList = new List <MethodBuilder>(); var wrapperList = new List <Func <IReadOnlyList <object>, IReadOnlyList <object> > >(); foreach (var signature in types) { var methodDef = wasmType.DefineMethod( $"func_{builderList.Count}", MethodAttributes.Public | MethodAttributes.Static); methodDef.SetParameters( signature.ParameterTypes.Select(ValueHelpers.ToClrType) .Concat(new[] { typeof(uint) }) .ToArray()); if (signature.ReturnTypes.Count == 0) { methodDef.SetReturnType(typeof(void)); } else if (signature.ReturnTypes.Count == 1) { methodDef.SetReturnType(ValueHelpers.ToClrType(signature.ReturnTypes[0])); } else { throw new WasmException("Cannot compile functions with more than one return value."); } builderList.Add(methodDef); } this.builders = builderList; this.wrappers = wrapperList; }
private bool TryCompile(FunctionType signature, FunctionBody body, ILGenerator generator) { var impl = GetImplementationOrNull(body.BodyInstructions); if (impl == null) { return(false); } else { var locals = new Dictionary <uint, LocalBuilder>(); var localTypes = new List <WasmValueType>(signature.ParameterTypes); uint localIndex = (uint)signature.ParameterTypes.Count; foreach (var item in body.Locals) { for (uint i = 0; i < item.LocalCount; i++) { locals[localIndex++] = generator.DeclareLocal(ValueHelpers.ToClrType(item.LocalType)); localTypes.Add(item.LocalType); } } var context = new CompilerContext(this, localTypes, signature.ParameterTypes.Count, locals); // Increment the call stack depth. generator.Emit(OpCodes.Ldarg, signature.ParameterTypes.Count); generator.Emit(OpCodes.Ldc_I4_1); generator.Emit(OpCodes.Add); generator.Emit(OpCodes.Starg, signature.ParameterTypes.Count); // Emit the method body. impl(context, generator); // Return. generator.Emit(OpCodes.Ret); return(true); } }
/// <summary> /// Compiles a 'select' instruction. /// </summary> /// <param name="instruction">The instruction to compile to an implementation.</param> public static InstructionImpl Select(Instruction instruction) { return((context, gen) => { context.Pop(); var rhsType = context.Pop(); var lhsType = context.Pop(); var ifLabel = gen.DefineLabel(); var endLabel = gen.DefineLabel(); gen.Emit(OpCodes.Brtrue, ifLabel); var rhsLocal = gen.DeclareLocal(ValueHelpers.ToClrType(rhsType)); gen.Emit(OpCodes.Stloc, rhsLocal); gen.Emit(OpCodes.Pop); gen.Emit(OpCodes.Ldloc, rhsLocal); gen.Emit(OpCodes.Br, endLabel); gen.MarkLabel(ifLabel); gen.Emit(OpCodes.Pop); gen.MarkLabel(endLabel); context.Push(lhsType); }); }
/// <summary> /// Compiles a 'call' instruction. /// </summary> /// <param name="instruction">The instruction to compile to an implementation.</param> public static InstructionImpl Call(Instruction instruction) { var callInsn = Operators.Call.CastInstruction(instruction); var index = (int)callInsn.Immediate; return((context, gen) => { // Check for stack overflow. var successLabel = gen.DefineLabel(); gen.Emit(OpCodes.Ldarg, context.ParameterCount); gen.Emit(OpCodes.Ldc_I4, (int)context.Compiler.module.Policy.MaxCallStackDepth); gen.Emit(OpCodes.Blt_Un, successLabel); gen.Emit(OpCodes.Ldstr, "A stack overflow occurred: the max call stack depth was exceeded"); gen.Emit(OpCodes.Ldstr, TrapException.SpecMessages.CallStackExhausted); gen.Emit(OpCodes.Newobj, typeof(TrapException).GetConstructor(new[] { typeof(string), typeof(string) })); gen.Emit(OpCodes.Throw); gen.MarkLabel(successLabel); FunctionType signature; if (index < context.Compiler.offset) { // If we're calling an import, then things get interesting. Basically, we have to emit // a call to the import's Invoke method instead of calling the import directly as // we can do with compiled methods. var callee = context.Compiler.module.Functions[index]; signature = new FunctionType(callee.ParameterTypes, callee.ReturnTypes); var args = new List <Func <ILGenerator, Type> >(); foreach (var item in callee.ParameterTypes.Reverse()) { var loc = gen.DeclareLocal(ValueHelpers.ToClrType(item)); gen.Emit(OpCodes.Stloc, loc); args.Add(generator => { generator.Emit(OpCodes.Ldloc, loc); return loc.LocalType; }); } args.Reverse(); context.Compiler.EmitExternalCall(gen, context.ParameterCount, callee, args); } else { // Push the call stack depth onto the evaluation stack. gen.Emit(OpCodes.Ldarg, context.ParameterCount); // Emit the call. signature = context.Compiler.types[index - context.Compiler.offset]; var callee = context.Compiler.builders[index - context.Compiler.offset]; gen.Emit(OpCodes.Call, callee); } // Pop and check argument types. var parameterTypes = signature.ParameterTypes; var paramTypesOnStack = context.Pop(parameterTypes.Count); for (int i = 0; i < parameterTypes.Count; i++) { if (parameterTypes[i] != paramTypesOnStack[i]) { throw new InvalidOperationException($"Expected type '{parameterTypes[i]}' on stack for function call, but got type '{paramTypesOnStack[i]}' instead."); } } // Update the type stack. foreach (var item in signature.ReturnTypes) { context.Push(item); } }); }