Пример #1
0
        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);
        }
Пример #2
0
        /// <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;
        }
Пример #3
0
        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);
            }
        }
Пример #4
0
        /// <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);
            });
        }
Пример #5
0
        /// <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);
                }
            });
        }