/// <summary> /// Reads a single CIL instruction from a given data stream. /// </summary> /// <param name="stream">The source data stream.</param> /// <param name="instruction">The instruction that was read, undefined if the method returns zero.</param> /// <returns>A value indicating if an instruction was read, <c>false</c> if the end of the stream was reached.</returns> public static bool ReadInstruction(Stream stream, out RawInstruction instruction) { Contract.Requires(stream != null && stream.CanRead); int firstOpcodeByte = stream.ReadByte(); if (unchecked ((byte)firstOpcodeByte) != firstOpcodeByte) { instruction = default(RawInstruction); return(false); } OpcodeValue opcodeValue; if (OpcodeValueEnum.IsFirstOfTwoBytes((byte)firstOpcodeByte)) { opcodeValue = (OpcodeValue)((firstOpcodeByte << 8) | stream.ReadUInt8()); } else { opcodeValue = (OpcodeValue)firstOpcodeByte; } var opcode = Opcode.FromValue(opcodeValue); if (opcode == Opcode.Switch) { // Read jump table int[] jumpTable = new int[stream.ReadUInt32()]; for (int i = 0; i < jumpTable.Length; ++i) { jumpTable[i] = stream.ReadInt32(); } instruction = RawInstruction.CreateSwitch(jumpTable); } else { NumericalOperand operand; switch ((int)opcode.OperandKind.GetSizeInBytes()) { case 0: operand = default(NumericalOperand); break; case 1: operand = stream.ReadInt8(); break; case 2: operand = stream.ReadInt16(); break; case 4: operand = stream.ReadInt32(); break; case 8: operand = stream.ReadInt64(); break; default: throw new NotSupportedException("Unexpected opcode operand size"); } instruction = new RawInstruction(opcode, operand); } return(true); }
private void EmitCall(ImmutableArray <Variable> targets, FunctionMethod function, ImmutableArray <Variable> arguments) { var signature = function.Signature; Contract.Assert(arguments.Length == signature.Inputs.Count); Contract.Assert(targets.Length <= signature.Outputs.Count); // Prepare the eventual return value store if (signature.HasReturnValue && targets.Length == 1) { BeginEmitStore(targets[0]); } // Push the input arguments for (int i = 0; i < arguments.Length; ++i) { var argument = arguments[i]; EmitLoad(argument); EmitConvert(argument.StaticRepr, signature.Inputs[i]); } if (signature.OutParameterCount > 0) { // Push the pointers to the output arguments for (int i = 0; i < signature.Outputs.Count; ++i) { if (i < targets.Length) { var target = targets[i]; var location = GetLocation(target); if (declaration.Outputs.Contains(target) && declaration.Outputs.Length >= 2) { cil.Load(location); // Target is a ByRef parameter, so already a (managed) pointer } else { cil.LoadAddress(location); } } else { throw new NotImplementedException("Ignored outputs of a multi-output function."); } } } // Call the function var method = function.Method; if (method is System.Reflection.Emit.MethodBuilder) { // We have to supply the parameter types ourselves since MethodBodyWriter // can't call MethodBuilder.GetParameters() without receiving an exception. cil.Call(Opcode.GetDefaultCall(method), method, function.Signature.GetParameterCliTypes(), function.Signature.ReturnCliType); } else { // We're calling a builtin var builtinCilOpcodeAttribute = method.GetCustomAttribute <BuiltinCilOpcodeAttribute>(); if (builtinCilOpcodeAttribute == null) { cil.Invoke(method); } else { // Calling the builtin is equivalent to emitting a sequence of CIL opcodes, so do that instead foreach (var opcodeValue in builtinCilOpcodeAttribute.Opcodes) { var opcode = Opcode.FromValue((OpcodeValue)opcodeValue); Contract.Assert(opcode != null); cil.Instruction(opcode); } } } // Handle the return value, if any if (signature.HasReturnValue) { if (targets.Length == 1) { EmitConvert(signature.Outputs[0], targets[0].StaticRepr); EndEmitStore(targets[0]); } else { // Return value ignored. Contract.Assert(targets.Length == 0); cil.Pop(); } } }