diff --git a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/ArgStack.cs b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/ArgStack.cs deleted file mode 100644 index 76ce9f352830..000000000000 --- a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/ArgStack.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections.Generic; -using LLVMSharp; - - -namespace Mono.Compiler.BigStep -{ - - public struct ArgStackValue { - public LLVMValueRef Ptr; - public BSType StoredType; - } - - - /// - /// The argument stack. - /// - /// We are going to want to clone this thing - /// - class ArgStack - { - //WISH: this really ought to be an immutable collection - private Stack data; - - public ArgStack () : this (new Stack ()) {} - - ArgStack (Stack data) { - this.data = data; - } - - public ArgStack Clone () { - ArgStackValue[] arr = new ArgStackValue[data.Count]; - data.CopyTo (arr, 0); - Array.Reverse (arr); - return new ArgStack (new Stack(arr)); - } - - public void Push (ArgStackValue x) - { - data.Push (x); - } - - public ArgStackValue Pop () - { - return data.Pop (); - } - - public int Count - { - get { return data.Count; } - } - } - -} diff --git a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/BSType.cs b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/BSType.cs deleted file mode 100644 index a954490b476d..000000000000 --- a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/BSType.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Reflection; - -using SimpleJit.Metadata; -using Mono.Compiler; - -using LLVMSharp; - -namespace Mono.Compiler.BigStep { - - /// - /// Tie together runtime types and LLVM types. - /// - public class BSType - { - ClrType rttype; - LLVMTypeRef? lowered; - - private BSType (ClrType t) - { - rttype = t; - } - - private BSType (BSType t, LLVMTypeRef l) - : this (t.rttype) - { - lowered = l; - } - - public static BSType FromClrType (ClrType t) - { - // TODO: cache? - return new BSType (t); - } - - public static BSType FromTypeInfo (TypeInfo t) - { - return FromClrType (RuntimeInformation.ClrTypeFromType (t)); - } - - public LLVMTypeRef Lowered { - get { - if (lowered != null) - return (LLVMTypeRef)lowered; - else - throw new NotImplementedException ("Don't know how to lower " + rttype.ToString ()); - } - } - - internal BSType LowerAs (LLVMTypeRef l) - { - return new BSType (this, l); - } - - - } - - /// - /// Some predefined BSType values from the runtime - /// - struct BSTypes { - public readonly BSType VoidType; - public readonly BSType Int32Type; - - internal BSTypes (IRuntimeInformation runtimeInfo) { - VoidType = BSType.FromClrType (runtimeInfo.VoidType).LowerAs (LLVMTypeRef.VoidType ()); - Int32Type = BSType.FromClrType (runtimeInfo.Int32Type).LowerAs (LLVMTypeRef.Int32Type ()); - } - } - -} diff --git a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/BigStep.cs b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/BigStep.cs index a4bf140b7ba7..0b3088a581c2 100644 --- a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/BigStep.cs +++ b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/BigStep.cs @@ -54,132 +54,19 @@ public CompilationResult CompileMethod (MethodInfo methodInfo, out NativeCodeHan } } - private CompilationResult CompileMethodOld (MethodInfo methodInfo, out NativeCodeHandle result) + internal static void InitializeLLVM_OSX_AMD64 (LLVMMCJITCompilerOptions mcjitCompilerOptions) { - var builder = new Builder (); - var env = new Env (RuntimeInfo, methodInfo); - - Preamble (env, builder); - - result = NativeCodeHandle.Invalid; - var r = TranslateBody (env, builder, methodInfo.Body); - if (r != Ok) - return r; - r = builder.Finish (out result); - return r; - } - - // translation environment for a single function - class Env { - private ArgStack currentStack; - private IRuntimeInformation RuntimeInfo { get; } - internal ArgStack ArgStack { get => currentStack ; } - public Env (IRuntimeInformation runtimeInfo, MethodInfo methodInfo) - { - this.RuntimeInfo = runtimeInfo; - this.MethodName = methodInfo.ClassInfo.Name + "::" + methodInfo.Name; - this.BSTypes = new BSTypes (runtimeInfo); - this.ReturnType = methodInfo.ReturnType; - - var parameters = methodInfo.Parameters; - this.ArgumentTypes = new ClrType [parameters.Count]; - int i = 0; - foreach (ParameterInfo pi in parameters) - this.ArgumentTypes [i++] = pi.ParameterType; - - currentStack = new ArgStack (); - } - - public ClrType ReturnType { get; } - public ClrType[] ArgumentTypes { get; } - - public readonly BSTypes BSTypes; - public readonly string MethodName; - } - - // encapsulate the LLVM module and builder here. - class Builder { - static readonly LLVMBool Success = new LLVMBool (0); - - LLVMModuleRef module; - LLVMBuilderRef builder; - LLVMValueRef function; - LLVMBasicBlockRef entry; - LLVMBasicBlockRef currentBB; - LLVMValueRef[] arguments; - - public LLVMModuleRef Module { get => module; } - public LLVMValueRef Function { get => function; } - - public Builder () { - module = LLVM.ModuleCreateWithName ("BigStepCompile"); - builder = LLVM.CreateBuilder (); - } - - public void BeginFunction (string name, BSType returnType, BSType[] args) { - var llvm_arguments = new LLVMTypeRef [args.Length]; - for (int i = 0; i < args.Length; i++) - llvm_arguments [i] = args [i].Lowered; - - var funTy = LLVM.FunctionType (returnType.Lowered, llvm_arguments, false); - function = LLVM.AddFunction (module, name, funTy); - entry = LLVM.AppendBasicBlock (function, "entry"); - LLVM.PositionBuilderAtEnd (builder, entry); - currentBB = entry; - - arguments = new LLVMValueRef [args.Length]; - for (int i = 0; i < args.Length; i++) { - arguments [i] = LLVM.GetParam (function, (uint) i); - LLVM.SetValueName (arguments [i], "arg" + i); - } - } - - internal unsafe void PrintDisassembly (NativeCodeHandle nch) { - IntPtr fnptr = new IntPtr (nch.Blob); - - // FIXME: do this once - LLVMDisasmContextRef disasm = LLVM.CreateDisasm (TargetTriple, IntPtr.Zero, 0, null, null); - LLVM.SetDisasmOptions (disasm, 2 /* print imm as hex */); - - // FIXME: use codebuf length - const long maxlength = 0x100; - long pc = 0; - - Console.WriteLine ("disasm:"); - while (pc < maxlength) { - const int stringBufSize = 0x40; - IntPtr outString = Marshal.AllocHGlobal (stringBufSize); - - long oldPc = pc; - pc += LLVM.DisasmInstruction (disasm, IntPtr.Add (fnptr, (int) pc), 0x100, 0, outString, stringBufSize); - - string s = Marshal.PtrToStringAnsi (outString); - - /* HACK because we don't know codbuf length; this is the disassembled string of 0x00 0x00 */ - if (s.Contains ("addb") && s.Contains ("%al, (%rax)")) { - break; - } - Console.WriteLine ($"{oldPc:x4}: {s}"); - } - } - - internal CompilationResult Finish (out NativeCodeHandle result) { - - // FIXME: get rid of this printf - LLVM.DumpModule (Module); - - //FIXME: do this once - LLVM.LinkInMCJIT (); - LLVM.InitializeX86TargetMC (); - LLVM.InitializeX86Target (); - LLVM.InitializeX86TargetInfo (); - LLVM.InitializeX86AsmParser (); - LLVM.InitializeX86AsmPrinter (); - LLVM.InitializeX86Disassembler (); - - /* this looks like unused code, but it initializes the target configuration */ - LLVMTargetRef target = LLVM.GetTargetFromName("x86-64"); - LLVMTargetMachineRef tmachine = LLVM.CreateTargetMachine( + LLVM.LinkInMCJIT (); + LLVM.InitializeX86TargetMC (); + LLVM.InitializeX86Target (); + LLVM.InitializeX86TargetInfo (); + LLVM.InitializeX86AsmParser (); + LLVM.InitializeX86AsmPrinter (); + LLVM.InitializeX86Disassembler (); + + /* this looks like unused code, but it initializes the target configuration */ + LLVMTargetRef target = LLVM.GetTargetFromName("x86-64"); + LLVMTargetMachineRef tmachine = LLVM.CreateTargetMachine( target, TargetTriple, "x86-64", // processor @@ -187,220 +74,9 @@ internal CompilationResult Finish (out NativeCodeHandle result) { LLVMCodeGenOptLevel.LLVMCodeGenLevelNone, LLVMRelocMode.LLVMRelocDefault, LLVMCodeModel.LLVMCodeModelDefault); - /* */ - - LLVMMCJITCompilerOptions options = new LLVMMCJITCompilerOptions { NoFramePointerElim = 0 }; - LLVM.InitializeMCJITCompilerOptions(options); - if (LLVM.CreateMCJITCompilerForModule(out var engine, Module, options, out var error) != Success) - { - /* FIXME: If I make completely bogus LLVM IR, I would expect to - * fail here and get some kind of error, but I don't. - */ - Console.Error.WriteLine($"Error: {error}"); - result = NativeCodeHandle.Invalid; - return CompilationResult.BadCode; - } - IntPtr fnptr = LLVM.GetPointerToGlobal (engine, Function); - if (fnptr == IntPtr.Zero) { - result = NativeCodeHandle.Invalid; - Console.Error.WriteLine ("LLVM.GetPointerToGlobal returned null"); - return CompilationResult.InternalError; - } else { - Console.Error.WriteLine ("saw {0}", fnptr); - } - unsafe { - result = new NativeCodeHandle ((byte*)fnptr, -1); - } - - // FIXME: guard behind debug flag - PrintDisassembly (result); - - //FIXME: cleanup in a Dispose method? - - LLVM.DisposeBuilder (builder); - - // FIXME: can I really dispose of the EE while code is installed in Mono :-( - - // LLVM.DisposeExecutionEngine (engine); - - return Ok; - } - - public LLVMValueRef ConstInt (BSType t, ulong value, bool signextend) - { - return LLVM.ConstInt (t.Lowered, value, signextend); - } - - public void EmitRetVoid () { - LLVM.BuildRetVoid (builder); - } - - public void EmitRet (LLVMValueRef v) - { - LLVM.BuildRet (builder, v); - } - - public LLVMValueRef EmitAlloca (BSType t, string nameHint) - { - return LLVM.BuildAlloca (builder, t.Lowered, nameHint); - } - - public LLVMValueRef EmitLoad (LLVMValueRef ptr, string nameHint) - { - return LLVM.BuildLoad (builder, ptr, nameHint); - } - - public LLVMValueRef EmitArgumentLoad (uint position) - { - return arguments [position]; - } - - public void EmitStore (LLVMValueRef value, LLVMValueRef ptr) - { - LLVM.BuildStore (builder, value, ptr); - } - - public LLVMValueRef EmitAdd (LLVMValueRef left, LLVMValueRef right) - { - return LLVM.BuildBinOp (builder, LLVMOpcode.LLVMAdd, left, right, "add"); - } - } - - void Preamble (Env env, Builder builder) - { - var rt = LowerType (env, env.ReturnType); - - BSType[] args = new BSType [env.ArgumentTypes.Length]; - for (int i = 0; i < env.ArgumentTypes.Length; i++) - args [i] = LowerType (env, env.ArgumentTypes [i]); - - builder.BeginFunction (env.MethodName, rt, args); - } - - CompilationResult TranslateBody (Env env, Builder builder, MethodBody body) - { - var iter = body.GetIterator (); - // TODO: alloca for locals and stack; store in env - - var r = Ok; - - while (iter.MoveNext ()) { - var opcode = iter.Opcode; - var opflags = iter.Flags; - switch (opcode) { - case Opcode.LdcI4_0: - case Opcode.LdcI4_1: - case Opcode.LdcI4_2: - r = TranslateLdcI4 (env, builder, opcode - Opcode.LdcI4_0); - break; - case Opcode.LdcI4S: - r = TranslateLdcI4 (env, builder, iter.DecodeParamI ()); - break; - case Opcode.Ldarg0: - case Opcode.Ldarg1: - r = TranslateLdarg (env, builder, opcode - Opcode.Ldarg0); - break; - case Opcode.Add: - // TODO: pass op - r = TranslateBinaryOp (env, builder); - break; - case Opcode.Ret: - r = TranslateRet (env, builder); - break; - default: - throw NIE ($"BigStep.Translate {opcode}"); - } - if (r != Ok) - break; - } - return r; - } - - CompilationResult TranslateRet (Env env, Builder builder) - { - if (env.ReturnType == RuntimeInfo.VoidType) { - builder.EmitRetVoid (); - return Ok; - } else { - var a = Pop (env, builder); - var v = builder.EmitLoad (a.Ptr, "ret-value"); - builder.EmitRet (v); - return Ok; - } - - } + /* */ - CompilationResult TranslateLdcI4 (Env env, Builder builder, System.Int32 c) - { - BSType t = env.BSTypes.Int32Type; - var a = Push (env, builder, t); - - var v = builder.ConstInt (t, (ulong)c, false); - builder.EmitStore (v, a.Ptr); - return Ok; - } - - CompilationResult TranslateLdarg (Env env, Builder builder, uint position) - { - var t = LowerType (env, env.ArgumentTypes [position]); - var a = Push (env, builder, t); - var v = builder.EmitArgumentLoad (position); - builder.EmitStore (v, a.Ptr); - return Ok; - } - - CompilationResult TranslateBinaryOp (Env env, Builder builder) - { - var a0 = Pop (env, builder); - var a1 = Pop (env, builder); - if (a0.StoredType != a1.StoredType) { - Console.Error.WriteLine ("BinOp: Types of operands do not match"); - return CompilationResult.InternalError; - } - - var v0 = builder.EmitLoad (a0.Ptr, "summand0"); - var v1 = builder.EmitLoad (a1.Ptr, "summand1"); - - var vr = builder.EmitAdd (v0, v1); - - var ar = Push (env, builder, a0.StoredType); - builder.EmitStore (vr, ar.Ptr); - - return Ok; - } - - ArgStackValue Push (Env env, Builder builder, BSType t) - { - // FIXME: create stack slots up front and just bump a - // stack height in the env and pick out the - // pre-allocated slot. - var v = builder.EmitAlloca (t, "stack-slot"); - var a = new ArgStackValue (); - a.Ptr = v; - a.StoredType = t; - env.ArgStack.Push (a); - return a; - } - - ArgStackValue Pop (Env env, Builder builder) - { - var a = env.ArgStack.Pop (); - return a; - } - - BSType LowerType (Env env, ClrType t) - { - if (t == RuntimeInfo.VoidType) - return env.BSTypes.VoidType; - else if (t == RuntimeInfo.Int32Type) - return env.BSTypes.Int32Type; - else - throw NIE ($"don't know how to lower type {t}"); - } - - private static Exception NIE (string msg) - { - return new NotImplementedException (msg); + LLVM.InitializeMCJITCompilerOptions(mcjitCompilerOptions); } } diff --git a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/CILSymbolicExecutor.cs b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/CILSymbolicExecutor.cs index d530b588aea5..30802646a039 100644 --- a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/CILSymbolicExecutor.cs +++ b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/CILSymbolicExecutor.cs @@ -5,399 +5,364 @@ using SimpleJit.Metadata; using SimpleJit.CIL; -namespace Mono.Compiler.BigStep -{ - /// - /// Emulate CIL execution only in the sense of stack change and delegates further handling for - /// each operation to a processor. - /// - /// - /// This class partially implements stack-based virtual machine as codified by ECMA-335. It tracks - /// stack depth change and types associated with each operand, which may come from stack, locals - /// or arguments. Upon completion of each operation it invokes a processor to perform customized - /// operation. LLVM bitcode emitter is implemented as a processor. - /// - public class CILSymbolicExecutor : INameGenerator - { - private IOperationProcessor processor; - private IRuntimeInformation runtime; - private MethodBody body; +namespace Mono.Compiler.BigStep { + /// + /// Emulate CIL execution only in the sense of stack change and delegates further handling for + /// each operation to a processor. + /// + /// + /// This class partially implements stack-based virtual machine as codified by ECMA-335. It tracks + /// stack depth change and types associated with each operand, which may come from stack, locals + /// or arguments. Upon completion of each operation it invokes a processor to perform customized + /// operation. LLVM bitcode emitter is implemented as a processor. + /// + public class CILSymbolicExecutor : INameGenerator { + private IOperationProcessor processor; + private IRuntimeInformation runtime; + private MethodBody body; - private Stack stack; - private int tempSeq = 0; + private Stack stack; + private int tempSeq = 0; - private List locals; - private List args; + private List locals; + private List args; - // Physical => Logical index mapping for instructions which are jump targets - private Dictionary targetIndices; + // Physical => Logical index mapping for instructions which are jump targets + private Dictionary targetIndices; - // INameGenerator - public string NextName() - { - return (tempSeq++).ToString(); - } + // INameGenerator + public string NextName () + { + return (tempSeq++).ToString (); + } - public CILSymbolicExecutor( - IOperationProcessor processor, - IRuntimeInformation runtime, - MethodInfo methodInfo) - { - this.processor = processor; - this.runtime = runtime; - this.body = methodInfo.Body; + public CILSymbolicExecutor(IOperationProcessor processor, IRuntimeInformation runtime, MethodInfo methodInfo) + { + this.processor = processor; + this.runtime = runtime; + this.body = methodInfo.Body; - this.stack = new Stack(); - this.targetIndices = new Dictionary(); + this.stack = new Stack(); + this.targetIndices = new Dictionary (); - // The input is already ordered by index. - this.locals = new List(); - foreach (LocalVariableInfo lvi in body.LocalVariables) - { - this.locals.Add(new LocalOperand(lvi.LocalIndex, lvi.LocalType)); - } + // The input is already ordered by index. + this.locals = new List (); + foreach (LocalVariableInfo lvi in body.LocalVariables) { + this.locals.Add (new LocalOperand (lvi.LocalIndex, lvi.LocalType)); + } - // The input is already ordered by index. - this.args = new List(); - foreach (ParameterInfo pi in methodInfo.Parameters) - { - this.args.Add(new ArgumentOperand(pi.Position, pi.ParameterType)); - } - } + // The input is already ordered by index. + this.args = new List (); + foreach (ParameterInfo pi in methodInfo.Parameters) { + this.args.Add (new ArgumentOperand (pi.Position, pi.ParameterType)); + } + } - public void Execute() - { - Pass1(); - Pass2(); - } + public void Execute () + { + Pass1 (); + Pass2 (); + } - /// In pass 1, collect jump information - private void Pass1() - { - // The jump info encoded in CIL is the byte offset within the method. But for the processor - // we want to represent jump target with the logical index of the first instruction in BB. - // To collect these info in one pass, use two data structures to cross-reference the logical-physical - // mapping. + /// In pass 1, collect jump information + private void Pass1 () + { + // The jump info encoded in CIL is the byte offset within the method. But for the processor + // we want to represent jump target with the logical index of the first instruction in BB. + // To collect these info in one pass, use two data structures to cross-reference the logical-physical + // mapping. - // logical => physical. This contains all instructions. - List lpIndices = new List(); - // physical => logical. This contains all instructions. - Dictionary plIndices = new Dictionary(); + // logical => physical. This contains all instructions. + List lpIndices = new List (); + // physical => logical. This contains all instructions. + Dictionary plIndices = new Dictionary (); - var iter = body.GetIterator(); - while (iter.MoveNext()) - { - Opcode opcode = iter.Opcode; - ExtendedOpcode? extOpCode = null; - if (opcode == Opcode.ExtendedPrefix) - { - extOpCode = iter.ExtOpcode; - } - OpcodeFlags opflags = iter.Flags; + var iter = body.GetIterator (); + while (iter.MoveNext ()) { + Opcode opcode = iter.Opcode; + ExtendedOpcode? extOpCode = null; + if (opcode == Opcode.ExtendedPrefix) { + extOpCode = iter.ExtOpcode; + } + OpcodeFlags opflags = iter.Flags; - // Record this instruction in both mappings - int pindex = iter.Index; - lpIndices.Add(pindex); - int lindex = lpIndices.Count - 1; - plIndices[pindex] = lindex; - // Check if there is an entry in targets for this instruction - if (targetIndices.ContainsKey(pindex)) - { - // If so, populate the entry with P index we just learned - targetIndices[pindex] = lindex; - } + // Record this instruction in both mappings + int pindex = iter.Index; + lpIndices.Add (pindex); + int lindex = lpIndices.Count - 1; + plIndices[pindex] = lindex; + // Check if there is an entry in targets for this instruction + if (targetIndices.ContainsKey (pindex)) { + // If so, populate the entry with P index we just learned + targetIndices[pindex] = lindex; + } - switch (opcode) - { - case Opcode.Br: - case Opcode.BrS: - case Opcode.Brfalse: - case Opcode.BrfalseS: - case Opcode.Brtrue: - case Opcode.BrtrueS: - int target = DecodeBranchTarget(iter); - if (target <= pindex) - { - // CASE I: Jump backward - // If jumping backward, we already have everything. - targetIndices[pindex] = plIndices[pindex]; - } - else - { - // CASE II: Jump forward - // If jumping forward, we don't know the logic index yet. The value - // will be filled later when we reach that instruction. - targetIndices[pindex] = -1; - } - break; - } - } - } + switch (opcode) { + case Opcode.Br: + case Opcode.BrS: + case Opcode.Brfalse: + case Opcode.BrfalseS: + case Opcode.Brtrue: + case Opcode.BrtrueS: + int target = DecodeBranchTarget(iter); + if (target <= pindex) { + // CASE I: Jump backward + // If jumping backward, we already have everything. + targetIndices[pindex] = plIndices[pindex]; + } else { + // CASE II: Jump forward + // If jumping forward, we don't know the logic index yet. The value + // will be filled later when we reach that instruction. + targetIndices[pindex] = -1; + } + break; + } + } + } - private int DecodeBranchTarget(IlIterator iter) - { - int opParam = iter.DecodeParamI(); - // Use next index to skip the bytes for the current instruction. - int target = iter.NextIndex + opParam; - return target; - } + private int DecodeBranchTarget (IlIterator iter) + { + int opParam = iter.DecodeParamI (); + // Use next index to skip the bytes for the current instruction. + int target = iter.NextIndex + opParam; + return target; + } - /// In pass 2, emulate execution - private void Pass2() - { - int index = 0; - var iter = body.GetIterator(); - while (iter.MoveNext()) - { - Opcode opcode = iter.Opcode; - ExtendedOpcode? extOpCode = null; - if (opcode == Opcode.ExtendedPrefix) - { - extOpCode = iter.ExtOpcode; - } - OpcodeFlags opflags = iter.Flags; - int opParam = 0; - IOperand output = null; + /// In pass 2, emulate execution + private void Pass2 () + { + int index = 0; + var iter = body.GetIterator (); + while (iter.MoveNext ()) { + Opcode opcode = iter.Opcode; + ExtendedOpcode? extOpCode = null; + if (opcode == Opcode.ExtendedPrefix) { + extOpCode = iter.ExtOpcode; + } + OpcodeFlags opflags = iter.Flags; + int opParam = 0; + IOperand output = null; - // 1) Collect operands - List operands = new List(); - // 1.1) operands not from stack - switch (opcode) - { - // 1.1.1) operands from Arguments - case Opcode.Ldarg0: - operands.Add(output = args[0]); - break; - case Opcode.Ldarg1: - operands.Add(output = args[1]); - break; - case Opcode.Ldarg2: - operands.Add(output = args[2]); - break; - case Opcode.Ldarg3: - operands.Add(output = args[3]); - break; - case Opcode.LdargS: - opParam = iter.DecodeParamI(); - operands.Add(output = args[opParam]); - break; - // 1.1.2) operands from Locals - case Opcode.Ldloc0: - operands.Add(output = locals[0]); - break; - case Opcode.Ldloc1: - operands.Add(output = locals[1]); - break; - case Opcode.Ldloc2: - operands.Add(output = locals[2]); - break; - case Opcode.Ldloc3: - operands.Add(output = locals[3]); - break; - case Opcode.LdlocS: - opParam = iter.DecodeParamI(); - operands.Add(output = locals[opParam]); - break; - // 1.1.3) operands from constants - case Opcode.LdcI4_0: - operands.Add(output = new Int32ConstOperand(0)); - break; - case Opcode.LdcI4_1: - operands.Add(output = new Int32ConstOperand(1)); - break; - case Opcode.LdcI4_2: - operands.Add(output = new Int32ConstOperand(2)); - break; - case Opcode.LdcI4_3: - operands.Add(output = new Int32ConstOperand(3)); - break; - case Opcode.LdcI4_4: - operands.Add(output = new Int32ConstOperand(4)); - break; - case Opcode.LdcI4_5: - operands.Add(new Int32ConstOperand(5)); - break; - case Opcode.LdcI4_6: - operands.Add(output = new Int32ConstOperand(6)); - break; - case Opcode.LdcI4_7: - operands.Add(output = new Int32ConstOperand(7)); - break; - case Opcode.LdcI4M1: - operands.Add(output = new Int32ConstOperand(-1)); - break; - case Opcode.LdcI4: - case Opcode.LdcI4S: - opParam = iter.DecodeParamI(); - operands.Add(output = new Int32ConstOperand(opParam)); - break; - case Opcode.LdcI8: - case Opcode.LdcR4: - case Opcode.LdcR8: - throw new Exception($"TODO: Cannot handle {opcode.ToString()} yet."); - // TODO: ExtendedOpcode.Ldloc - } + // 1) Collect operands + List operands = new List (); + // 1.1) operands not from stack + switch (opcode) { + // 1.1.1) operands from Arguments + case Opcode.Ldarg0: + operands.Add (output = args[0]); + break; + case Opcode.Ldarg1: + operands.Add (output = args[1]); + break; + case Opcode.Ldarg2: + operands.Add (output = args[2]); + break; + case Opcode.Ldarg3: + operands.Add (output = args[3]); + break; + case Opcode.LdargS: + opParam = iter.DecodeParamI (); + operands.Add (output = args[opParam]); + break; + // 1.1.2) operands from Locals + case Opcode.Ldloc0: + operands.Add (output = locals[0]); + break; + case Opcode.Ldloc1: + operands.Add (output = locals[1]); + break; + case Opcode.Ldloc2: + operands.Add (output = locals[2]); + break; + case Opcode.Ldloc3: + operands.Add (output = locals[3]); + break; + case Opcode.LdlocS: + opParam = iter.DecodeParamI(); + operands.Add (output = locals[opParam]); + break; + // 1.1.3) operands from constants + case Opcode.LdcI4_0: + operands.Add (output = new Int32ConstOperand (0)); + break; + case Opcode.LdcI4_1: + operands.Add (output = new Int32ConstOperand (1)); + break; + case Opcode.LdcI4_2: + operands.Add (output = new Int32ConstOperand (2)); + break; + case Opcode.LdcI4_3: + operands.Add (output = new Int32ConstOperand (3)); + break; + case Opcode.LdcI4_4: + operands.Add (output = new Int32ConstOperand (4)); + break; + case Opcode.LdcI4_5: + operands.Add (new Int32ConstOperand (5)); + break; + case Opcode.LdcI4_6: + operands.Add (output = new Int32ConstOperand(6)); + break; + case Opcode.LdcI4_7: + operands.Add (output = new Int32ConstOperand(7)); + break; + case Opcode.LdcI4M1: + operands.Add (output = new Int32ConstOperand(-1)); + break; + case Opcode.LdcI4: + case Opcode.LdcI4S: + opParam = iter.DecodeParamI(); + operands.Add (output = new Int32ConstOperand(opParam)); + break; + case Opcode.LdcI8: + case Opcode.LdcR4: + case Opcode.LdcR8: + throw new Exception($"TODO: Cannot handle {opcode.ToString()} yet."); + // TODO: ExtendedOpcode.Ldloc + } - // 1.2) operands to be popped from stack - PopBehavior popbhv = iter.PopBehavior; - int popCount = 0; - switch (popbhv) - { - case PopBehavior.Pop0: - popCount = 0; - break; - case PopBehavior.Pop1: - case PopBehavior.Popi: - case PopBehavior.Popref: - popCount = 1; - break; - case PopBehavior.Pop1_pop1: - case PopBehavior.Popi_popi: - case PopBehavior.Popi_popi8: - case PopBehavior.Popi_popr4: - case PopBehavior.Popi_popr8: - case PopBehavior.Popref_pop1: - case PopBehavior.Popi_pop1: - case PopBehavior.Popref_popi: - popCount = 2; - break; - case PopBehavior.Popi_popi_popi: - case PopBehavior.Popref_popi_popi: - case PopBehavior.Popref_popi_popi8: - case PopBehavior.Popref_popi_popr4: - case PopBehavior.Popref_popi_popr8: - case PopBehavior.Popref_popi_popref: - popCount = 3; - break; - case PopBehavior.PopAll: - popCount = stack.Count; - break; - case PopBehavior.Varpop: - if (opcode == Opcode.Ret) - { - if (stack.Count == 0) - { - break; - } - else if (stack.Count == 1) - { - popCount = 1; - break; - } - else if (stack.Count > 1) - { - // Likely a bug somewhere else in the symbolic engine. - throw new Exception($"Unexpected. Leaves function with non-empty stack."); - } - } + // 1.2) operands to be popped from stack + PopBehavior popbhv = iter.PopBehavior; + int popCount = 0; + switch (popbhv) { + case PopBehavior.Pop0: + popCount = 0; + break; + case PopBehavior.Pop1: + case PopBehavior.Popi: + case PopBehavior.Popref: + popCount = 1; + break; + case PopBehavior.Pop1_pop1: + case PopBehavior.Popi_popi: + case PopBehavior.Popi_popi8: + case PopBehavior.Popi_popr4: + case PopBehavior.Popi_popr8: + case PopBehavior.Popref_pop1: + case PopBehavior.Popi_pop1: + case PopBehavior.Popref_popi: + popCount = 2; + break; + case PopBehavior.Popi_popi_popi: + case PopBehavior.Popref_popi_popi: + case PopBehavior.Popref_popi_popi8: + case PopBehavior.Popref_popi_popr4: + case PopBehavior.Popref_popi_popr8: + case PopBehavior.Popref_popi_popref: + popCount = 3; + break; + case PopBehavior.PopAll: + popCount = stack.Count; + break; + case PopBehavior.Varpop: + if (opcode == Opcode.Ret) { + if (stack.Count == 0) { + break; + } else if (stack.Count == 1) { + popCount = 1; + break; + } else if (stack.Count > 1) { + // Likely a bug somewhere else in the symbolic engine. + throw new Exception ($"Unexpected. Leaves function with non-empty stack."); + } + } - throw new Exception( - $"TODO: Cannot handle PopBehavior.Varpop against { opcode } yet."); - } + throw new Exception ($"TODO: Cannot handle PopBehavior.Varpop against { opcode } yet."); + } - int count = popCount; - ClrType[] exprOdTypes = new ClrType[count]; - while (count > 0) - { - TempOperand tmp = stack.Pop(); - operands.Add(tmp); - exprOdTypes[count - 1] = tmp.Type; - count--; - } + int count = popCount; + ClrType[] exprOdTypes = new ClrType[count]; + while (count > 0) { + TempOperand tmp = stack.Pop (); + operands.Add (tmp); + exprOdTypes[count - 1] = tmp.Type; + count--; + } - // Additional operands - switch (opcode) - { - case Opcode.Br: - case Opcode.BrS: - case Opcode.Brfalse: - case Opcode.BrfalseS: - case Opcode.Brtrue: - case Opcode.BrtrueS: - int target = DecodeBranchTarget(iter); - int logicIndex = targetIndices[target]; - BranchTargetOperand bto = new BranchTargetOperand(logicIndex); - operands.Add(bto); - break; - case Opcode.Stloc0: - operands.Add(locals[0]); - break; - case Opcode.Stloc1: - operands.Add(locals[1]); - break; - case Opcode.Stloc2: - operands.Add(locals[2]); - break; - case Opcode.Stloc3: - operands.Add(locals[3]); - break; - case Opcode.StlocS: - opParam = iter.DecodeParamI(); - operands.Add(locals[opParam]); - break; - // TODO: ExtendedOpcode.Stloc - } + // Additional operands + switch (opcode) { + case Opcode.Br: + case Opcode.BrS: + case Opcode.Brfalse: + case Opcode.BrfalseS: + case Opcode.Brtrue: + case Opcode.BrtrueS: + int target = DecodeBranchTarget (iter); + int logicIndex = targetIndices[target]; + BranchTargetOperand bto = new BranchTargetOperand (logicIndex); + operands.Add (bto); + break; + case Opcode.Stloc0: + operands.Add (locals[0]); + break; + case Opcode.Stloc1: + operands.Add (locals[1]); + break; + case Opcode.Stloc2: + operands.Add (locals[2]); + break; + case Opcode.Stloc3: + operands.Add (locals[3]); + break; + case Opcode.StlocS: + opParam = iter.DecodeParamI (); + operands.Add (locals[opParam]); + break; + // TODO: ExtendedOpcode.Stloc + } - // 2) Determine the result type for values to push into stack - TempOperand tod = null; - if (output != null) - { - tod = new TempOperand(this, output.Type); - } - else - { - ClrType? ctyp = OpResultTypeLookup.Query(opcode, extOpCode, exprOdTypes); - if (ctyp.HasValue) - { - tod = new TempOperand(this, (ClrType)ctyp); - } - } + // 2) Determine the result type for values to push into stack + TempOperand tod = null; + if (output != null) { + tod = new TempOperand (this, output.Type); + } else { + ClrType? ctyp = OpResultTypeLookup.Query (opcode, extOpCode, exprOdTypes); + if (ctyp.HasValue) { + tod = new TempOperand(this, (ClrType)ctyp); + } + } - // 3) Push result - PushBehavior pushbhv = iter.PushBehavior; - switch (pushbhv) - { - case PushBehavior.Push0: - break; - case PushBehavior.Push1: - case PushBehavior.Pushi: - case PushBehavior.Pushi8: - case PushBehavior.Pushr4: - case PushBehavior.Pushr8: - if (tod == null) - { - throw new Exception("Unexpected: no value to push to stack."); - } - stack.Push(tod); - break; - case PushBehavior.Push1_push1: - // This only applies to Opcode.Dup - if (tod == null) - { - throw new Exception("Unexpected: no value to push to stack."); - } - stack.Push(tod); - stack.Push(tod); - break; - case PushBehavior.Varpush: - // This is a huge TODO. Function call (Opcode.Call/Calli/Callvirt) relies on this behavior. - throw new Exception("TODO: Cannot handle PushBehavior.Varpush yet."); - } + // 3) Push result + PushBehavior pushbhv = iter.PushBehavior; + switch (pushbhv) { + case PushBehavior.Push0: + break; + case PushBehavior.Push1: + case PushBehavior.Pushi: + case PushBehavior.Pushi8: + case PushBehavior.Pushr4: + case PushBehavior.Pushr8: + if (tod == null) { + throw new Exception ("Unexpected: no value to push to stack."); + } + stack.Push (tod); + break; + case PushBehavior.Push1_push1: + // This only applies to Opcode.Dup + if (tod == null) { + throw new Exception ("Unexpected: no value to push to stack."); + } + stack.Push(tod); + stack.Push(tod); + break; + case PushBehavior.Varpush: + // This is a huge TODO. Function call (Opcode.Call/Calli/Callvirt) relies on this behavior. + throw new Exception ("TODO: Cannot handle PushBehavior.Varpush yet."); + } - // 4) Send the info to operation processor - bool isJumpTarget = targetIndices.ContainsKey(iter.Index); - OperationInfo opInfo = new OperationInfo - { - Index = index, - Operation = opcode, - ExtOperation = extOpCode, - Operands = operands.ToArray(), - Result = tod, - JumpTarget = isJumpTarget - }; - processor.Process(opInfo); - index++; - } - } - } -} \ No newline at end of file + // 4) Send the info to operation processor + bool isJumpTarget = targetIndices.ContainsKey(iter.Index); + OperationInfo opInfo = new OperationInfo { + Index = index, + Operation = opcode, + ExtOperation = extOpCode, + Operands = operands.ToArray (), + Result = tod, + JumpTarget = isJumpTarget + }; + processor.Process (opInfo); + index++; + } + } + } +} diff --git a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/LLVM/BitCodeEmitter.cs b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/LLVM/BitCodeEmitter.cs index 46d9adae0043..0c1dc1c17923 100644 --- a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/LLVM/BitCodeEmitter.cs +++ b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/LLVM/BitCodeEmitter.cs @@ -11,602 +11,490 @@ /// /// Emit LLVM bitcode via LLVMSharp. /// -namespace Mono.Compiler.BigStep.LLVMBackend -{ - public class BitCodeEmitter : IOperationProcessor - { - private static readonly LLVMBool Success = new LLVMBool(0); - private static readonly LLVMBool True = new LLVMBool(1); - private static readonly LLVMMCJITCompilerOptions s_options = new LLVMMCJITCompilerOptions { NoFramePointerElim = 0 }; - - private static int s_moduleSeq; - private static bool s_uninitialized; - - private LLVMModuleRef module; - private LLVMBuilderRef builder; - private LLVMValueRef function; - - private LLVMValueRef[] argAddrs; - private LLVMValueRef[] localAddrs; - private Dictionary temps; - private Dictionary bbs; - - public bool PrintDebugInfo { get; set; } - public bool VerifyGeneratedCode { get; set; } - - public BitCodeEmitter(MethodInfo method) - { - int seq = Interlocked.Increment(ref s_moduleSeq); - string modName = "llvmmodule_" + seq; - module = LLVM.ModuleCreateWithName(modName); - builder = LLVM.CreateBuilder(); - temps = new Dictionary(); - bbs = new Dictionary(); - - IReadOnlyCollection prms = method.Parameters; - LLVMTypeRef[] largs = new LLVMTypeRef[prms.Count]; - int i = 0; - foreach (ParameterInfo pinfo in prms) - { - largs[i] = TranslateType(pinfo.ParameterType); - i++; - } - LLVMTypeRef rtyp = TranslateType(method.ReturnType); - - var funTy = LLVM.FunctionType(rtyp, largs, false); - string funcName = modName + "_" + method.Name; - function = LLVM.AddFunction(module, funcName, funTy); - CreateFirstBasicBlock(); - - IList locals = method.Body.LocalVariables; - AllocateArgsAndLocals(largs, locals); - } - - #region Basic Block operations - private void CreateFirstBasicBlock() - { - LLVMBasicBlockRef bb = LLVM.AppendBasicBlock(function, "entry"); - LLVM.PositionBuilderAtEnd(builder, bb); - } - - private LLVMBasicBlockRef GetOrAddBasicBlock(int opIndex, bool moveToEnd) - { - string name = "BB_" + opIndex; - LLVMBasicBlockRef bbr; - if (!bbs.TryGetValue(name, out bbr)) - { - bbs[name] = bbr = LLVM.AppendBasicBlock(function, name); - } - - if (moveToEnd) - { - LLVM.PositionBuilderAtEnd(builder, bbr); - } - - return bbr; - } - #endregion - - private void AllocateArgsAndLocals(LLVMTypeRef[] args, IList locals) - { - this.argAddrs = new LLVMValueRef[args.Length]; - uint i = 0; - for (; i < args.Length; i++) - { - LLVMValueRef vref = LLVM.GetParam(function, i); - LLVMValueRef vaddr = LLVM.BuildAlloca(builder, args[i], "A" + i); - LLVM.BuildStore(builder, vref, vaddr); - this.argAddrs[i] = vaddr; - } - - i = 0; - localAddrs = new LLVMValueRef[locals.Count]; - foreach (LocalVariableInfo lvi in locals) - { - LLVMTypeRef ltyp = TranslateType(lvi.LocalType); - LLVMValueRef lref = LLVM.BuildAlloca(builder, ltyp, "L" + i); - this.localAddrs[i] = lref; - } - } - - // Emit LLVM instruction per CIL operation - public void Process(OperationInfo opInfo) - { - if (opInfo.JumpTarget) - { - // If this op is a jump target, replace BB now - this.GetOrAddBasicBlock(opInfo.Index, true); - } - - Opcode op = opInfo.Operation; - ExtendedOpcode? exop = opInfo.ExtOperation; - IOperand[] operands = opInfo.Operands; - - // The result is the value pushed onto the stack by CLR at the end of instruction. - // In the translation we treat each new frame on the stack as a distinct instance - // that corresponds to a register in LLVM. If a frame is popped and pushed again it - // becomes a new instance. - // - // CLR eval-stack frame = TempOperand = LLVM temp register - // - // If the result is non-null, we must generate a new temp value and associate it - // with the temp operand's name. When a temp operand appears in the operands, it - // means a previously pushed value is being consumed by instruction. Based on the - // name of the operand we can retrieve the temp register and use that in LLVM - // operation. - string tempName = opInfo.Result?.Name; - - switch (op) - { - // Notation for comments: - // op1, op2, ... => result pushed into expr-stack - case Opcode.Nop: - break; - case Opcode.Ret: - // tmp - InvokeOperation( - op, exop, operands, - vm => - { - if (operands.Length > 0) - { - LLVM.BuildRet(builder, vm.Temp0); - } - else - { - LLVM.BuildRetVoid(builder); - } - }); - break; - case Opcode.Ldarg0: - case Opcode.Ldarg1: - case Opcode.Ldarg2: - case Opcode.Ldarg3: - case Opcode.LdargS: - // arg => tmp - InvokeOperation( - op, exop, operands, - vm => - { - LLVMValueRef tmp = LLVM.BuildLoad(builder, vm.Address0, tempName); - return new NamedTempValue(tmp, tempName); - }); - break; - case Opcode.Stloc0: - case Opcode.Stloc1: - case Opcode.Stloc2: - case Opcode.Stloc3: - // tmp, local - InvokeOperation( - op, exop, operands, - vm => - { - LLVM.BuildStore(builder, vm.Temp0, vm.Address1); - }); - break; - case Opcode.LdcI4: - case Opcode.LdcI4_0: - case Opcode.LdcI4_1: - case Opcode.LdcI4_2: - case Opcode.LdcI4_3: - case Opcode.LdcI4_4: - case Opcode.LdcI4_5: - case Opcode.LdcI4_6: - case Opcode.LdcI4_7: - case Opcode.LdcI4_8: - case Opcode.LdcI4M1: - case Opcode.LdcI4S: - // const => tmp - InvokeOperation( - op, exop, operands, - vm => - { - // LLVM doesn't allow assignment from constant to value. - // So we just pretend that the constant is a temp value. - // When it's used in an instruction it will be realized - // in the form of "ty value-literal" (e.g. "i32 42") - LLVMValueRef tmp = vm.Const0; - return new NamedTempValue(tmp, tempName); - }); - break; - case Opcode.Ldloc0: - case Opcode.Ldloc1: - case Opcode.Ldloc2: - case Opcode.Ldloc3: - case Opcode.LdlocS: - // local => tmp - InvokeOperation( - op, exop, operands, - vm => - { - LLVMValueRef tmp = LLVM.BuildLoad(builder, vm.Address0, tempName); - return new NamedTempValue(tmp, tempName); - }); - break; - case Opcode.Add: - case Opcode.AddOvf: // TODO - Handle overflow - case Opcode.AddOvfUn: // TODO - Handle overflow, unsigned - // tmp, tmp => tmp - InvokeOperation( - op, exop, operands, - vm => - { - LLVMValueRef tmp = LLVM.BuildAdd(builder, vm.Temp0, vm.Temp1, tempName); - return new NamedTempValue(tmp, tempName); - }); - break; - case Opcode.Sub: - case Opcode.SubOvf: // TODO - Handle overflow - case Opcode.SubOvfUn: // TODO - Handle overflow, unsigned - // tmp, tmp => tmp - InvokeOperation( - op, exop, operands, - vm => - { - LLVMValueRef tmp = LLVM.BuildSub(builder, vm.Temp0, vm.Temp1, tempName); - return new NamedTempValue(tmp, tempName); - }); - break; - case Opcode.Mul: - case Opcode.MulOvf: // TODO - Handle overflow - case Opcode.MulOvfUn: // TODO - Handle overflow, unsigned - // tmp, tmp => tmp - InvokeOperation( - op, exop, operands, - vm => - { - LLVMValueRef tmp = LLVM.BuildMul(builder, vm.Temp0, vm.Temp1, tempName); - return new NamedTempValue(tmp, tempName); - }); - break; - case Opcode.Div: - // tmp, tmp => tmp - InvokeOperation( - op, exop, operands, - vm => - { - LLVMValueRef tmp = LLVM.BuildFDiv(builder, vm.Temp0, vm.Temp1, tempName); - return new NamedTempValue(tmp, tempName); - }); - break; - case Opcode.Br: - case Opcode.BrS: - // Special - jump to another BB - LLVMBasicBlockRef bb = this.GetBranchTarget(operands[0]); - LLVM.BuildBr(builder, bb); - break; - case Opcode.DivUn: - // tmp, tmp => tmp - InvokeOperation( - op, exop, operands, - vm => - { - LLVMValueRef tmp = LLVM.BuildUDiv(builder, vm.Temp0, vm.Temp1, tempName); - return new NamedTempValue(tmp, tempName); - }); - break; - } - } - - /// Produce a native handle for the generated native code. Call this after Process() - public NativeCodeHandle Yield() - { - if (PrintDebugInfo) - { - LLVM.DumpModule(module); - } - - if (!BitCodeEmitter.s_uninitialized) - { - lock (typeof(BitCodeEmitter)) - { - if (!BitCodeEmitter.s_uninitialized) - { - LLVM.LinkInMCJIT(); - LLVM.InitializeX86TargetMC(); - LLVM.InitializeX86Target(); - LLVM.InitializeX86TargetInfo(); - LLVM.InitializeX86AsmParser(); - LLVM.InitializeX86AsmPrinter(); - LLVM.InitializeMCJITCompilerOptions(s_options); - - BitCodeEmitter.s_uninitialized = true; +namespace Mono.Compiler.BigStep.LLVMBackend { + public class BitCodeEmitter : IOperationProcessor { + private static readonly LLVMBool Success = new LLVMBool (0); + private static readonly LLVMBool True = new LLVMBool (1); + private static readonly LLVMMCJITCompilerOptions s_options = new LLVMMCJITCompilerOptions { NoFramePointerElim = 0 }; + + private static int s_moduleSeq; + private static bool s_initialized; + + private LLVMModuleRef module; + private LLVMBuilderRef builder; + private LLVMValueRef function; + + private LLVMValueRef[] argAddrs; + private LLVMValueRef[] localAddrs; + private Dictionary temps; + private Dictionary bbs; + + public bool PrintDebugInfo { get; set; } + public bool VerifyGeneratedCode { get; set; } + + public BitCodeEmitter (MethodInfo method) + { + int seq = Interlocked.Increment (ref s_moduleSeq); + string modName = "llvmmodule_" + seq; + module = LLVM.ModuleCreateWithName(modName); + builder = LLVM.CreateBuilder (); + temps = new Dictionary (); + bbs = new Dictionary (); + + IReadOnlyCollection prms = method.Parameters; + LLVMTypeRef[] largs = new LLVMTypeRef[prms.Count]; + int i = 0; + foreach (ParameterInfo pinfo in prms) { + largs[i] = TranslateType (pinfo.ParameterType); + i++; + } + LLVMTypeRef rtyp = TranslateType (method.ReturnType); + + var funTy = LLVM.FunctionType (rtyp, largs, false); + string funcName = modName + "_" + method.Name; + function = LLVM.AddFunction (module, funcName, funTy); + CreateFirstBasicBlock (); + + IList locals = method.Body.LocalVariables; + AllocateArgsAndLocals (largs, locals); + } + + #region Basic Block operations + private void CreateFirstBasicBlock () + { + LLVMBasicBlockRef bb = LLVM.AppendBasicBlock (function, "entry"); + LLVM.PositionBuilderAtEnd (builder, bb); + } + + private LLVMBasicBlockRef GetOrAddBasicBlock (int opIndex, bool moveToEnd) + { + string name = "BB_" + opIndex; + LLVMBasicBlockRef bbr; + if (!bbs.TryGetValue (name, out bbr)) { + bbs[name] = bbr = LLVM.AppendBasicBlock (function, name); + } + + if (moveToEnd) + LLVM.PositionBuilderAtEnd (builder, bbr); + + return bbr; + } + #endregion + + private void AllocateArgsAndLocals (LLVMTypeRef[] args, IList locals) + { + this.argAddrs = new LLVMValueRef[args.Length]; + uint i = 0; + for (; i < args.Length; i++) { + LLVMValueRef vref = LLVM.GetParam (function, i); + LLVMValueRef vaddr = LLVM.BuildAlloca (builder, args[i], "A" + i); + LLVM.BuildStore (builder, vref, vaddr); + this.argAddrs[i] = vaddr; + } + + i = 0; + localAddrs = new LLVMValueRef[locals.Count]; + foreach (LocalVariableInfo lvi in locals) { + LLVMTypeRef ltyp = TranslateType (lvi.LocalType); + LLVMValueRef lref = LLVM.BuildAlloca (builder, ltyp, "L" + i); + this.localAddrs[i++] = lref; + } + } + + // Emit LLVM instruction per CIL operation + public void Process (OperationInfo opInfo) + { + if (opInfo.JumpTarget) { + // If this op is a jump target, replace BB now + this.GetOrAddBasicBlock (opInfo.Index, true); + } + + Opcode op = opInfo.Operation; + ExtendedOpcode? exop = opInfo.ExtOperation; + IOperand[] operands = opInfo.Operands; + + // The result is the value pushed onto the stack by CLR at the end of instruction. + // In the translation we treat each new frame on the stack as a distinct instance + // that corresponds to a register in LLVM. If a frame is popped and pushed again it + // becomes a new instance. + // + // CLR eval-stack frame = TempOperand = LLVM temp register + // + // If the result is non-null, we must generate a new temp value and associate it + // with the temp operand's name. When a temp operand appears in the operands, it + // means a previously pushed value is being consumed by instruction. Based on the + // name of the operand we can retrieve the temp register and use that in LLVM + // operation. + string tempName = opInfo.Result?.Name; + + switch (op) { + // Notation for comments: + // op1, op2, ... => result pushed into expr-stack + case Opcode.Nop: + break; + case Opcode.Ret: + // tmp + InvokeOperation (op, exop, operands, + vm => { + if (operands.Length > 0) { + LLVM.BuildRet (builder, vm.Temp0); + } else { + LLVM.BuildRetVoid (builder); + } + }); + break; + case Opcode.Ldarg0: + case Opcode.Ldarg1: + case Opcode.Ldarg2: + case Opcode.Ldarg3: + case Opcode.LdargS: + // arg => tmp + InvokeOperation (op, exop, operands, + vm => { + LLVMValueRef tmp = LLVM.BuildLoad (builder, vm.Address0, tempName); + return new NamedTempValue (tmp, tempName); + }); + break; + case Opcode.Stloc0: + case Opcode.Stloc1: + case Opcode.Stloc2: + case Opcode.Stloc3: + // tmp, local + InvokeOperation (op, exop, operands, + vm => { + LLVM.BuildStore (builder, vm.Temp0, vm.Address1); + }); + break; + case Opcode.LdcI4: + case Opcode.LdcI4_0: + case Opcode.LdcI4_1: + case Opcode.LdcI4_2: + case Opcode.LdcI4_3: + case Opcode.LdcI4_4: + case Opcode.LdcI4_5: + case Opcode.LdcI4_6: + case Opcode.LdcI4_7: + case Opcode.LdcI4_8: + case Opcode.LdcI4M1: + case Opcode.LdcI4S: + // const => tmp + InvokeOperation (op, exop, operands, + vm => { + // LLVM doesn't allow assignment from constant to value. + // So we just pretend that the constant is a temp value. + // When it's used in an instruction it will be realized + // in the form of "ty value-literal" (e.g. "i32 42") + LLVMValueRef tmp = vm.Const0; + return new NamedTempValue (tmp, tempName); + }); + break; + case Opcode.Ldloc0: + case Opcode.Ldloc1: + case Opcode.Ldloc2: + case Opcode.Ldloc3: + case Opcode.LdlocS: + // local => tmp + InvokeOperation (op, exop, operands, + vm => { + LLVMValueRef tmp = LLVM.BuildLoad (builder, vm.Address0, tempName); + return new NamedTempValue (tmp, tempName); + }); + break; + case Opcode.Add: + case Opcode.AddOvf: // TODO - Handle overflow + case Opcode.AddOvfUn: // TODO - Handle overflow, unsigned + // tmp, tmp => tmp + InvokeOperation (op, exop, operands, + vm => { + LLVMValueRef tmp = LLVM.BuildAdd (builder, vm.Temp0, vm.Temp1, tempName); + return new NamedTempValue (tmp, tempName); + }); + break; + case Opcode.Sub: + case Opcode.SubOvf: // TODO - Handle overflow + case Opcode.SubOvfUn: // TODO - Handle overflow, unsigned + // tmp, tmp => tmp + InvokeOperation (op, exop, operands, + vm => { + LLVMValueRef tmp = LLVM.BuildSub (builder, vm.Temp0, vm.Temp1, tempName); + return new NamedTempValue (tmp, tempName); + }); + break; + case Opcode.Mul: + case Opcode.MulOvf: // TODO - Handle overflow + case Opcode.MulOvfUn: // TODO - Handle overflow, unsigned + // tmp, tmp => tmp + InvokeOperation(op, exop, operands, + vm => { + LLVMValueRef tmp = LLVM.BuildMul (builder, vm.Temp0, vm.Temp1, tempName); + return new NamedTempValue (tmp, tempName); + }); + break; + case Opcode.Div: + // tmp, tmp => tmp + InvokeOperation (op, exop, operands, + vm => { + LLVMValueRef tmp = LLVM.BuildFDiv (builder, vm.Temp0, vm.Temp1, tempName); + return new NamedTempValue (tmp, tempName); + }); + break; + case Opcode.Br: + case Opcode.BrS: + // Special - jump to another BB + LLVMBasicBlockRef bb = this.GetBranchTarget (operands[0]); + LLVM.BuildBr(builder, bb); + break; + case Opcode.DivUn: + // tmp, tmp => tmp + InvokeOperation (op, exop, operands, + vm => { + LLVMValueRef tmp = LLVM.BuildUDiv (builder, vm.Temp0, vm.Temp1, tempName); + return new NamedTempValue (tmp, tempName); + }); + break; + } + } + + /// Produce a native handle for the generated native code. Call this after Process() + public NativeCodeHandle Yield () + { + if (PrintDebugInfo) + LLVM.DumpModule (module); + + if (!BitCodeEmitter.s_initialized) { + lock (typeof (BitCodeEmitter)) { + if (!BitCodeEmitter.s_initialized) { + BigStep.InitializeLLVM_OSX_AMD64 (s_options); + + BitCodeEmitter.s_initialized = true; //Console.WriteLine("[DEBUG] LLVM initialized."); - } - } - } - - try - { - if (VerifyGeneratedCode) - { - if (LLVM.VerifyFunction( - function, LLVMVerifierFailureAction.LLVMPrintMessageAction) != Success) - { - throw new Exception($"Couldn't verify the generated code. There is likely due to bug in code generation."); } } + } + + try { + if (VerifyGeneratedCode) { + if (LLVM.VerifyFunction (function, LLVMVerifierFailureAction.LLVMPrintMessageAction) != Success) + throw new Exception ($"Couldn't verify the generated code. There is likely due to bug in code generation."); + } - if (LLVM.CreateMCJITCompilerForModule( - out LLVMExecutionEngineRef engine, module, s_options, out var error) != Success) - { - throw new Exception($"Compilation by LLVM failed: { error }"); - } + if (LLVM.CreateMCJITCompilerForModule(out LLVMExecutionEngineRef engine, module, s_options, out var error) != Success) + throw new Exception ($"Compilation by LLVM failed: { error }"); //Console.WriteLine("[DEBUG] LLVM compilation succeeded."); - IntPtr fnptr = LLVM.GetPointerToGlobal(engine, function); - unsafe - { - return new NativeCodeHandle((byte*)fnptr, -1); - } - } - finally - { - LLVM.DisposeBuilder(builder); - } - } - - internal class ValueMappings - { - internal LLVMValueRef Const0 - { - get - { - return Values[0].Value; - } - } - - internal LLVMValueRef Address0 - { - get - { - return Values[0].Value; - } - } - - internal LLVMValueRef Address1 - { - get - { - return Values[0].Value; - } - } - - internal LLVMValueRef Temp0 - { - get - { - return Values[0].Value; - } - } - - internal LLVMValueRef Temp1 - { - get - { - return Values[1].Value; - } - } - - internal StorageTypedValue[] Values { get; private set; } - - internal ValueMappings(int length) - { - Values = new StorageTypedValue[length]; - } - } - - internal enum StorageType - { - Address, - Temp, - Const - } - - internal class StorageTypedValue - { - internal StorageType Type { get; set; } - internal LLVMValueRef Value { get; set; } - } - - internal class NamedTempValue - { - internal string Name { get; set; } - internal LLVMValueRef Value { get; set; } - - internal NamedTempValue(LLVMValueRef value, string name = null) - { - this.Value = value; - this.Name = name; - } - } - - /// Invoke an LLVM operation with given value mappings. - /// The operation is supposed to return a temp value to be stored. - private void InvokeOperation( - Opcode op, - ExtendedOpcode? exop, - IOperand[] operands, - Func emitFunc) - { - if (this.PrintDebugInfo) + IntPtr fnptr = LLVM.GetPointerToGlobal (engine, function); + unsafe { + return new NativeCodeHandle((byte*)fnptr, -1); + } + } finally { + LLVM.DisposeBuilder(builder); + } + } + + internal class ValueMappings { + internal LLVMValueRef Const0 { + get { return Values[0].Value; } + } + + internal LLVMValueRef Address0 { + get { return Values[0].Value; } + } + + internal LLVMValueRef Address1 { + get { return Values[0].Value; } + } + + internal LLVMValueRef Temp0 { + get { return Values[0].Value; } + } + + internal LLVMValueRef Temp1 { + get { return Values[1].Value; } + } + + internal StorageTypedValue[] Values { get; private set; } + + internal ValueMappings (int length) + { + Values = new StorageTypedValue[length]; + } + } + + internal enum StorageType { + Address, + Temp, + Const + } + + internal class StorageTypedValue { + internal StorageType Type { get; set; } + internal LLVMValueRef Value { get; set; } + } + + internal class NamedTempValue { + internal string Name { get; set; } + internal LLVMValueRef Value { get; set; } + + internal NamedTempValue (LLVMValueRef value, string name = null) { + this.Value = value; + this.Name = name; + } + } + + /// Invoke an LLVM operation with given value mappings. + /// The operation is supposed to return a temp value to be stored. + private void InvokeOperation (Opcode op, ExtendedOpcode? exop, IOperand[] operands, Func emitFunc) + { + if (this.PrintDebugInfo) { string opstr = $"[DEBUG] {op.ToString()} - "; - foreach (IOperand od in operands) - { + foreach (IOperand od in operands) { opstr += od.Name; opstr += " "; } - Console.WriteLine(opstr); - } - - ValueMappings mappings = new ValueMappings(operands.Length); - StorageTypedValue[] stvalues = mappings.Values; - for (int i = 0; i < operands.Length; i++) - { - IOperand operand = operands[i]; - stvalues[i] = MakeStorageTypedValue(operand); - } - - NamedTempValue ntv = emitFunc(mappings); - if (ntv != null) - { - string name = ntv.Name; - if (name == null) - { - name = LLVM.GetValueName(ntv.Value); - } - temps[name] = ntv.Value; - } - } - - /// Invoke an LLVM operation with given value mappings. - /// The operation doesn't produce new temp values. - private void InvokeOperation( - Opcode op, - ExtendedOpcode? exop, - IOperand[] operands, - Action emitFunc) - { - InvokeOperation( - op, - exop, - operands, - (vm) => - { - emitFunc(vm); - return null; - }); - } - - private StorageTypedValue MakeStorageTypedValue(IOperand operand) - { - LLVMValueRef value = default(LLVMValueRef); - StorageType stype = default(StorageType); - switch (operand.OperandType) - { - case OperandType.Temp: - stype = StorageType.Temp; - value = GetTempValue(operand); - break; - case OperandType.Local: - stype = StorageType.Address; - value = GetLocalAddr(operand); - break; - case OperandType.Argument: - stype = StorageType.Address; - value = GetArgAddr(operand); - break; - case OperandType.Const: - stype = StorageType.Const; - value = GetConstValue(operand); - break; - } - return new StorageTypedValue - { - Type = stype, - Value = value - }; - } - - private LLVMBasicBlockRef GetBranchTarget(IOperand operand) - { - BranchTargetOperand bto = (BranchTargetOperand)operand; - int target = bto.PC; - return GetOrAddBasicBlock(target, false); - } - - private LLVMValueRef GetArgAddr(IOperand operand) - { - ArgumentOperand aod = (ArgumentOperand)operand; - return this.argAddrs[aod.Index]; - } - - private LLVMValueRef GetLocalAddr(IOperand operand) - { - LocalOperand lod = (LocalOperand)operand; - return this.localAddrs[lod.Index]; - } - - private LLVMValueRef GetTempValue(IOperand operand) - { - TempOperand tod = (TempOperand)operand; - string name = tod.Name; - return temps[tod.Name]; - } - - private LLVMValueRef GetConstValue(IOperand operand) - { - ConstOperand cod = (ConstOperand)operand; - if (cod is Int32ConstOperand) - { - return LLVM.ConstInt(LLVM.Int32Type(), (ulong)((Int32ConstOperand)cod).Value, true); - } - if (cod is Int64ConstOperand) - { - return LLVM.ConstInt(LLVM.Int64Type(), (ulong)((Int64ConstOperand)cod).Value, true); - } - if (cod is Float32ConstOperand) - { - return LLVM.ConstReal(LLVM.FloatType(), ((Float32ConstOperand)cod).Value); - } - if (cod is Float64ConstOperand) - { - return LLVM.ConstReal(LLVM.FloatType(), ((Float64ConstOperand)cod).Value); - } - - throw new Exception("Unexpected. The const operand is tno recognized."); - } - - private static LLVMTypeRef TranslateType(ClrType ctyp) - { - if (ctyp == RuntimeInformation.BoolType) - { - return LLVM.Int1Type(); - } - if (ctyp == RuntimeInformation.Int8Type) - { - return LLVM.Int8Type(); - } - if (ctyp == RuntimeInformation.Int16Type || ctyp == RuntimeInformation.Int8Type) - { - return LLVM.Int16Type(); - } - if (ctyp == RuntimeInformation.Int32TypeInstance || ctyp == RuntimeInformation.UInt16Type) - { - return LLVM.Int32Type(); - } - if (ctyp == RuntimeInformation.Int64Type || ctyp == RuntimeInformation.UInt32Type) - { - return LLVM.Int64Type(); - } - if (ctyp == RuntimeInformation.CharType) - { - return LLVM.Int16Type(); // Unicode - } - if (ctyp == RuntimeInformation.Float32Type || ctyp == RuntimeInformation.Float64Type) - { - return LLVM.FloatType(); - } - if (ctyp == RuntimeInformation.NativeIntType || ctyp == RuntimeInformation.NativeUnsignedIntType) - { - return LLVM.Int64Type(); - } - if (ctyp == RuntimeInformation.StringType) - { - return LLVM.PointerType(LLVM.Int16Type(), 0); // 0 = default address sapce - } - if (ctyp == RuntimeInformation.VoidTypeInstance) - { - return LLVM.VoidType(); - } - - Type typ = ctyp.AsSystemType; - if (typ.IsClass) - { - return LLVM.PointerType(LLVM.Int64Type(), 0); // 0 = default address sapce - } - - throw new Exception($"TODO: Cannot handle type { typ.Name } yet."); - } - } -} \ No newline at end of file + Console.WriteLine (opstr); + } + + ValueMappings mappings = new ValueMappings (operands.Length); + StorageTypedValue[] stvalues = mappings.Values; + for (int i = 0; i < operands.Length; i++) { + IOperand operand = operands[i]; + stvalues[i] = MakeStorageTypedValue (operand); + } + + NamedTempValue ntv = emitFunc (mappings); + if (ntv != null) { + string name = ntv.Name; + if (name == null) { + name = LLVM.GetValueName (ntv.Value); + } + temps[name] = ntv.Value; + } + } + + /// Invoke an LLVM operation with given value mappings. + /// The operation doesn't produce new temp values. + private void InvokeOperation (Opcode op, ExtendedOpcode? exop, IOperand[] operands, Action emitFunc) + { + InvokeOperation (op, exop, operands, + (vm) => { + emitFunc (vm); + return null; + }); + } + + private StorageTypedValue MakeStorageTypedValue (IOperand operand) + { + LLVMValueRef value = default (LLVMValueRef); + StorageType stype = default (StorageType); + switch (operand.OperandType) { + case OperandType.Temp: + stype = StorageType.Temp; + value = GetTempValue (operand); + break; + case OperandType.Local: + stype = StorageType.Address; + value = GetLocalAddr (operand); + break; + case OperandType.Argument: + stype = StorageType.Address; + value = GetArgAddr (operand); + break; + case OperandType.Const: + stype = StorageType.Const; + value = GetConstValue (operand); + break; + } + return new StorageTypedValue { + Type = stype, + Value = value + }; + } + + private LLVMBasicBlockRef GetBranchTarget (IOperand operand) + { + BranchTargetOperand bto = (BranchTargetOperand)operand; + int target = bto.PC; + return GetOrAddBasicBlock (target, false); + } + + private LLVMValueRef GetArgAddr (IOperand operand) + { + ArgumentOperand aod = (ArgumentOperand)operand; + return this.argAddrs[aod.Index]; + } + + private LLVMValueRef GetLocalAddr (IOperand operand) + { + LocalOperand lod = (LocalOperand)operand; + return this.localAddrs[lod.Index]; + } + + private LLVMValueRef GetTempValue (IOperand operand) + { + TempOperand tod = (TempOperand)operand; + string name = tod.Name; + return temps[tod.Name]; + } + + private LLVMValueRef GetConstValue (IOperand operand) + { + ConstOperand cod = (ConstOperand)operand; + if (cod is Int32ConstOperand) { + return LLVM.ConstInt (LLVM.Int32Type (), (ulong)((Int32ConstOperand)cod).Value, true); + } + if (cod is Int64ConstOperand) { + return LLVM.ConstInt (LLVM.Int64Type (), (ulong)((Int64ConstOperand)cod).Value, true); + } + if (cod is Float32ConstOperand) + { + return LLVM.ConstReal (LLVM.FloatType (), ((Float32ConstOperand)cod).Value); + } + if (cod is Float64ConstOperand) { + return LLVM.ConstReal (LLVM.FloatType (), ((Float64ConstOperand)cod).Value); + } + + throw new Exception ("Unexpected. The const operand is tno recognized."); + } + + private static LLVMTypeRef TranslateType (ClrType ctyp) + { + if (ctyp == RuntimeInformation.BoolType) { + return LLVM.Int1Type (); + } + if (ctyp == RuntimeInformation.Int8Type) { + return LLVM.Int8Type (); + } + if (ctyp == RuntimeInformation.Int16Type || ctyp == RuntimeInformation.Int8Type) { + return LLVM.Int16Type (); + } + if (ctyp == RuntimeInformation.Int32TypeInstance || ctyp == RuntimeInformation.UInt16Type) { + return LLVM.Int32Type(); + } + if (ctyp == RuntimeInformation.Int64Type || ctyp == RuntimeInformation.UInt32Type) { + return LLVM.Int64Type (); + } + if (ctyp == RuntimeInformation.CharType) { + return LLVM.Int16Type (); // Unicode + } + if (ctyp == RuntimeInformation.Float32Type || ctyp == RuntimeInformation.Float64Type) { + return LLVM.FloatType (); + } + if (ctyp == RuntimeInformation.NativeIntType || ctyp == RuntimeInformation.NativeUnsignedIntType) { + return LLVM.Int64Type (); + } + if (ctyp == RuntimeInformation.StringType) { + return LLVM.PointerType(LLVM.Int16Type (), 0); // 0 = default address sapce + } + if (ctyp == RuntimeInformation.VoidTypeInstance) { + return LLVM.VoidType(); + } + + Type typ = ctyp.AsSystemType; + if (typ.IsClass) { + return LLVM.PointerType(LLVM.Int64Type (), 0); // 0 = default address sapce + } + + throw new Exception ($"TODO: Cannot handle type { typ.Name } yet."); + } + } +} diff --git a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/OpResultTypeLookup.cs b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/OpResultTypeLookup.cs index 0ef5717acf51..fbac9b6a6103 100644 --- a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/OpResultTypeLookup.cs +++ b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/OpResultTypeLookup.cs @@ -8,267 +8,226 @@ /// /// Implemented "III.1.5 Operand type table" /// -namespace Mono.Compiler.BigStep -{ - // III.1.5 Operand type table - internal class OpResultTypeLookup - { - internal static ClrType? Query(Opcode op, ExtendedOpcode? exop, params ClrType[] types) - { - if (exop.HasValue) - { - ExtendedOpcode exopcode = (ExtendedOpcode)exop; - switch (exop) - { - // Table III.4: Binary Comparison or Branch Operations - case ExtendedOpcode.Ceq: - case ExtendedOpcode.Cgt: - case ExtendedOpcode.CgtUn: - case ExtendedOpcode.Clt: - case ExtendedOpcode.CltUn: - return RuntimeInformation.BoolType; - } - } +namespace Mono.Compiler.BigStep { + // III.1.5 Operand type table + internal class OpResultTypeLookup { + internal static ClrType? Query (Opcode op, ExtendedOpcode? exop, params ClrType[] types) + { + if (exop.HasValue) { + ExtendedOpcode exopcode = (ExtendedOpcode)exop; + switch (exop) { + // Table III.4: Binary Comparison or Branch Operations + case ExtendedOpcode.Ceq: + case ExtendedOpcode.Cgt: + case ExtendedOpcode.CgtUn: + case ExtendedOpcode.Clt: + case ExtendedOpcode.CltUn: + return RuntimeInformation.BoolType; + } + } - switch (op) - { - // Table III.2: Binary Numeric Operations - // Table III.7: Overflow Arithmetic Operations - case Opcode.Add: - case Opcode.AddOvf: - case Opcode.AddOvfUn: - case Opcode.Sub: - case Opcode.SubOvf: - case Opcode.SubOvfUn: - case Opcode.Mul: - case Opcode.MulOvf: - case Opcode.MulOvfUn: - case Opcode.Div: - case Opcode.Rem: - return QueryBinaryOp(types[0], types[1]); - // Table III.3: Unary Numeric Operations - case Opcode.Neg: - if (types[0] == RuntimeInformation.Int32TypeInstance && types[1] == RuntimeInformation.Int32TypeInstance) - { - return RuntimeInformation.BoolType; - } - if (types[0] == RuntimeInformation.Int64Type && types[1] == RuntimeInformation.Int64Type) - { - return RuntimeInformation.BoolType; - } - if (types[0] == RuntimeInformation.TypedRefType && types[1] == RuntimeInformation.TypedRefType) - { - return RuntimeInformation.BoolType; - } - if ((types[0] == RuntimeInformation.Float32Type || types[0] == RuntimeInformation.Float64Type) && - (types[1] == RuntimeInformation.Float32Type || types[1] == RuntimeInformation.Float64Type)) - { - return RuntimeInformation.BoolType; - } - // This should never happen unless there are bugs in CSC. - throw new Exception($"Unexpected. Operation { op.ToString() } cannot perform on operands of type { types[0].AsSystemType.Name } and { types[1].AsSystemType.Name }"); - // Table III.4: Binary Comparison or Branch Operations - case Opcode.Beq: - case Opcode.BeqS: - case Opcode.Bge: - case Opcode.BgeS: - case Opcode.BgeUn: - case Opcode.BgeUnS: - case Opcode.Bgt: - case Opcode.BgtS: - case Opcode.BgtUn: - case Opcode.BgtUnS: - case Opcode.Ble: - case Opcode.BleS: - case Opcode.BleUn: - case Opcode.BleUnS: - case Opcode.Blt: - case Opcode.BltS: - case Opcode.BltUn: - case Opcode.BltUnS: - case Opcode.BneUn: - case Opcode.BneUnS: - return RuntimeInformation.BoolType; - // Table III.5: Table III.5: Integer Operations - case Opcode.And: - case Opcode.Not: - case Opcode.Or: - case Opcode.Xor: - case Opcode.RemUn: - case Opcode.DivUn: - // Reuse matrix defined for bianry ops since the valid set is a subset of the latter - // and we always assume the validaity of input operands. - return QueryBinaryOp(types[0], types[1]); - // Table III.6: Shift Operations - case Opcode.Shl: - case Opcode.Shr: - case Opcode.ShrUn: - // operand 0: To Be Shifted - // operand 1: Shift-By - if (types[0] == RuntimeInformation.Int32TypeInstance && - (types[1] == RuntimeInformation.Int32TypeInstance || types[1] == RuntimeInformation.NativeIntType)) - { - return RuntimeInformation.Int32TypeInstance; - } - if (types[0] == RuntimeInformation.Int64Type && - (types[1] == RuntimeInformation.Int32TypeInstance || types[1] == RuntimeInformation.NativeIntType)) - { - return RuntimeInformation.Int64Type; - } - if (types[0] == RuntimeInformation.NativeIntType && - (types[1] == RuntimeInformation.Int32TypeInstance || types[1] == RuntimeInformation.NativeIntType)) - { - return RuntimeInformation.NativeIntType; - } - // This should never happen unless there are bugs in CSC. - throw new Exception($"Unexpected. Operation { op.ToString() } cannot perform on operands of type { types[0].AsSystemType.Name } and { types[1].AsSystemType.Name }"); - // Table III.8: Conversion Operations - case Opcode.ConvI1: - case Opcode.ConvU1: - case Opcode.ConvI2: - case Opcode.ConvU2: - case Opcode.ConvI4: - case Opcode.ConvU4: - case Opcode.ConvOvfI1: - case Opcode.ConvOvfI1Un: - case Opcode.ConvOvfI2: - case Opcode.ConvOvfI2Un: - case Opcode.ConvOvfI4: - case Opcode.ConvOvfI4Un: - case Opcode.ConvI: - case Opcode.ConvOvfI: - case Opcode.ConvU: - case Opcode.ConvOvfU: - // For short integers,the stack value is truncated but remains the same type. - return types[0]; - case Opcode.ConvI8: - case Opcode.ConvU8: - if (types[0] == RuntimeInformation.Float32Type || - (types[0] == RuntimeInformation.Int32TypeInstance)) - { - return RuntimeInformation.Int64Type; - } - else - { - return types[0]; - } - case Opcode.ConvR4: - case Opcode.ConvR8: - case Opcode.ConvRUn: - return RuntimeInformation.Float64Type; - default: - return null; - } - } + switch (op) { + // Table III.2: Binary Numeric Operations + // Table III.7: Overflow Arithmetic Operations + case Opcode.Add: + case Opcode.AddOvf: + case Opcode.AddOvfUn: + case Opcode.Sub: + case Opcode.SubOvf: + case Opcode.SubOvfUn: + case Opcode.Mul: + case Opcode.MulOvf: + case Opcode.MulOvfUn: + case Opcode.Div: + case Opcode.Rem: + return QueryBinaryOp (types[0], types[1]); + // Table III.3: Unary Numeric Operations + case Opcode.Neg: + if (types[0] == RuntimeInformation.Int32TypeInstance && types[1] == RuntimeInformation.Int32TypeInstance) { + return RuntimeInformation.BoolType; + } + if (types[0] == RuntimeInformation.Int64Type && types[1] == RuntimeInformation.Int64Type) { + return RuntimeInformation.BoolType; + } + if (types[0] == RuntimeInformation.TypedRefType && types[1] == RuntimeInformation.TypedRefType) { + return RuntimeInformation.BoolType; + } + if ((types[0] == RuntimeInformation.Float32Type || types[0] == RuntimeInformation.Float64Type) && + (types[1] == RuntimeInformation.Float32Type || types[1] == RuntimeInformation.Float64Type)) { + return RuntimeInformation.BoolType; + } + // This should never happen unless there are bugs in CSC. + throw new Exception ($"Unexpected. Operation { op.ToString () } cannot perform on operands of type { types[0].AsSystemType.Name } and { types[1].AsSystemType.Name }"); + // Table III.4: Binary Comparison or Branch Operations + case Opcode.Beq: + case Opcode.BeqS: + case Opcode.Bge: + case Opcode.BgeS: + case Opcode.BgeUn: + case Opcode.BgeUnS: + case Opcode.Bgt: + case Opcode.BgtS: + case Opcode.BgtUn: + case Opcode.BgtUnS: + case Opcode.Ble: + case Opcode.BleS: + case Opcode.BleUn: + case Opcode.BleUnS: + case Opcode.Blt: + case Opcode.BltS: + case Opcode.BltUn: + case Opcode.BltUnS: + case Opcode.BneUn: + case Opcode.BneUnS: + return RuntimeInformation.BoolType; + // Table III.5: Table III.5: Integer Operations + case Opcode.And: + case Opcode.Not: + case Opcode.Or: + case Opcode.Xor: + case Opcode.RemUn: + case Opcode.DivUn: + // Reuse matrix defined for bianry ops since the valid set is a subset of the latter + // and we always assume the validaity of input operands. + return QueryBinaryOp (types[0], types[1]); + // Table III.6: Shift Operations + case Opcode.Shl: + case Opcode.Shr: + case Opcode.ShrUn: + // operand 0: To Be Shifted + // operand 1: Shift-By + if (types[0] == RuntimeInformation.Int32TypeInstance && + (types[1] == RuntimeInformation.Int32TypeInstance || types[1] == RuntimeInformation.NativeIntType)) { + return RuntimeInformation.Int32TypeInstance; + } + if (types[0] == RuntimeInformation.Int64Type && + (types[1] == RuntimeInformation.Int32TypeInstance || types[1] == RuntimeInformation.NativeIntType)) { + return RuntimeInformation.Int64Type; + } + if (types[0] == RuntimeInformation.NativeIntType && + (types[1] == RuntimeInformation.Int32TypeInstance || types[1] == RuntimeInformation.NativeIntType)) { + return RuntimeInformation.NativeIntType; + } + // This should never happen unless there are bugs in CSC. + throw new Exception ($"Unexpected. Operation { op.ToString () } cannot perform on operands of type { types[0].AsSystemType.Name } and { types[1].AsSystemType.Name }"); + // Table III.8: Conversion Operations + case Opcode.ConvI1: + case Opcode.ConvU1: + case Opcode.ConvI2: + case Opcode.ConvU2: + case Opcode.ConvI4: + case Opcode.ConvU4: + case Opcode.ConvOvfI1: + case Opcode.ConvOvfI1Un: + case Opcode.ConvOvfI2: + case Opcode.ConvOvfI2Un: + case Opcode.ConvOvfI4: + case Opcode.ConvOvfI4Un: + case Opcode.ConvI: + case Opcode.ConvOvfI: + case Opcode.ConvU: + case Opcode.ConvOvfU: + // For short integers,the stack value is truncated but remains the same type. + return types[0]; + case Opcode.ConvI8: + case Opcode.ConvU8: + if (types[0] == RuntimeInformation.Float32Type || + (types[0] == RuntimeInformation.Int32TypeInstance)) { + return RuntimeInformation.Int64Type; + } else { + return types[0]; + } + case Opcode.ConvR4: + case Opcode.ConvR8: + case Opcode.ConvRUn: + return RuntimeInformation.Float64Type; + default: + return null; + } + } - internal class CilTypePair - { - private string notation; + internal class CilTypePair { + private string notation; - private CilTypePair(ClrType ta, ClrType tb) - { - string sa = ClrTypeToString(ta); - string sb = ClrTypeToString(tb); - notation = sb + "-" + sb; - } + private CilTypePair (ClrType ta, ClrType tb) + { + string sa = ClrTypeToString (ta); + string sb = ClrTypeToString (tb); + notation = sb + "-" + sb; + } - public override int GetHashCode() - { - return notation.GetHashCode(); - } + public override int GetHashCode () + { + return notation.GetHashCode (); + } - public override bool Equals(object obj) - { - if (obj != null && obj is CilTypePair) - { - return notation == ((CilTypePair)obj).notation; - } + public override bool Equals (object obj) + { + if (obj != null && obj is CilTypePair) { + return notation == ((CilTypePair)obj).notation; + } - return false; - } + return false; + } - internal static CilTypePair Create(ClrType ta, ClrType tb) - { - return new CilTypePair(ta, tb); - } - } + internal static CilTypePair Create (ClrType ta, ClrType tb) + { + return new CilTypePair (ta, tb); + } + } - // Convert the type to one of five types tracked by CLI for operator result verification - private static String ClrTypeToString(ClrType type) - { - if (type == RuntimeInformation.Int32TypeInstance) - { - return "int32"; - } - if (type == RuntimeInformation.Int64Type) - { - return "int64"; - } - if (type == RuntimeInformation.NativeIntType) - { - return "nativeint"; - } - else if (type == RuntimeInformation.TypedRefType) - { - return "&"; // Is this even correct? - } - else - { - Type t = type.AsSystemType; - if (t.IsClass) - { - return "O"; - } - else - { - return "?"; - } - } - } + // Convert the type to one of five types tracked by CLI for operator result verification + private static String ClrTypeToString (ClrType type) + { + if (type == RuntimeInformation.Int32TypeInstance) { + return "int32"; + } + if (type == RuntimeInformation.Int64Type) { + return "int64"; + } + if (type == RuntimeInformation.NativeIntType) { + return "nativeint"; + } else if (type == RuntimeInformation.TypedRefType) { + return "&"; // Is this even correct? + } else { + Type t = type.AsSystemType; + if (t.IsClass) { + return "O"; + } else { + return "?"; + } + } + } - internal static ClrType QueryBinaryOp(ClrType ta, ClrType tb) - { - return binaryOpResults[CilTypePair.Create(ta, tb)]; - } + internal static ClrType QueryBinaryOp (ClrType ta, ClrType tb) + { + return binaryOpResults[CilTypePair.Create (ta, tb)]; + } - private static Dictionary binaryOpResults; + private static Dictionary binaryOpResults; - static OpResultTypeLookup() - { - binaryOpResults = new Dictionary(); - binaryOpResults[CilTypePair.Create(RuntimeInformation.Int32TypeInstance, RuntimeInformation.Int32TypeInstance)] - = RuntimeInformation.Int32TypeInstance; - binaryOpResults[CilTypePair.Create(RuntimeInformation.Int64Type, RuntimeInformation.Int64Type)] - = RuntimeInformation.Int64Type; - binaryOpResults[CilTypePair.Create(RuntimeInformation.Int32TypeInstance, RuntimeInformation.NativeIntType)] - = RuntimeInformation.NativeIntType; - binaryOpResults[CilTypePair.Create(RuntimeInformation.NativeIntType, RuntimeInformation.Int32TypeInstance)] - = RuntimeInformation.NativeIntType; - binaryOpResults[CilTypePair.Create(RuntimeInformation.NativeIntType, RuntimeInformation.NativeIntType)] - = RuntimeInformation.NativeIntType; + static OpResultTypeLookup () + { + binaryOpResults = new Dictionary (); + binaryOpResults[CilTypePair.Create(RuntimeInformation.Int32TypeInstance, RuntimeInformation.Int32TypeInstance)] = RuntimeInformation.Int32TypeInstance; + binaryOpResults[CilTypePair.Create(RuntimeInformation.Int64Type, RuntimeInformation.Int64Type)] = RuntimeInformation.Int64Type; + binaryOpResults[CilTypePair.Create(RuntimeInformation.Int32TypeInstance, RuntimeInformation.NativeIntType)] = RuntimeInformation.NativeIntType; + binaryOpResults[CilTypePair.Create(RuntimeInformation.NativeIntType, RuntimeInformation.Int32TypeInstance)] = RuntimeInformation.NativeIntType; + binaryOpResults[CilTypePair.Create(RuntimeInformation.NativeIntType, RuntimeInformation.NativeIntType)] = RuntimeInformation.NativeIntType; - // For F types, always result in long type for now. This needs re-visiting - binaryOpResults[CilTypePair.Create(RuntimeInformation.Float32Type, RuntimeInformation.Float32Type)] - = RuntimeInformation.Float64Type; - binaryOpResults[CilTypePair.Create(RuntimeInformation.Float32Type, RuntimeInformation.Float64Type)] - = RuntimeInformation.Float64Type; - binaryOpResults[CilTypePair.Create(RuntimeInformation.Float64Type, RuntimeInformation.Float32Type)] - = RuntimeInformation.Float64Type; - binaryOpResults[CilTypePair.Create(RuntimeInformation.Float64Type, RuntimeInformation.Float64Type)] - = RuntimeInformation.Float64Type; + // For F types, always result in long type for now. This needs re-visiting + binaryOpResults[CilTypePair.Create(RuntimeInformation.Float32Type, RuntimeInformation.Float32Type)] = RuntimeInformation.Float64Type; + binaryOpResults[CilTypePair.Create(RuntimeInformation.Float32Type, RuntimeInformation.Float64Type)] = RuntimeInformation.Float64Type; + binaryOpResults[CilTypePair.Create(RuntimeInformation.Float64Type, RuntimeInformation.Float32Type)] = RuntimeInformation.Float64Type; + binaryOpResults[CilTypePair.Create(RuntimeInformation.Float64Type, RuntimeInformation.Float64Type)] = RuntimeInformation.Float64Type; - // For & types, they are only applicable to certain OPs. - // But since we assume the validity of the input, do not perform such checks. - binaryOpResults[CilTypePair.Create(RuntimeInformation.Int32TypeInstance, RuntimeInformation.TypedRefType)] - = RuntimeInformation.TypedRefType; - binaryOpResults[CilTypePair.Create(RuntimeInformation.NativeIntType, RuntimeInformation.TypedRefType)] - = RuntimeInformation.TypedRefType; - binaryOpResults[CilTypePair.Create(RuntimeInformation.TypedRefType, RuntimeInformation.Int32TypeInstance)] - = RuntimeInformation.TypedRefType; - binaryOpResults[CilTypePair.Create(RuntimeInformation.TypedRefType, RuntimeInformation.NativeIntType)] - = RuntimeInformation.TypedRefType; - binaryOpResults[CilTypePair.Create(RuntimeInformation.TypedRefType, RuntimeInformation.TypedRefType)] - = RuntimeInformation.TypedRefType; - } - } -} \ No newline at end of file + // For & types, they are only applicable to certain OPs. + // But since we assume the validity of the input, do not perform such checks. + binaryOpResults[CilTypePair.Create(RuntimeInformation.Int32TypeInstance, RuntimeInformation.TypedRefType)] = RuntimeInformation.TypedRefType; + binaryOpResults[CilTypePair.Create(RuntimeInformation.NativeIntType, RuntimeInformation.TypedRefType)] = RuntimeInformation.TypedRefType; + binaryOpResults[CilTypePair.Create(RuntimeInformation.TypedRefType, RuntimeInformation.Int32TypeInstance)] = RuntimeInformation.TypedRefType; + binaryOpResults[CilTypePair.Create(RuntimeInformation.TypedRefType, RuntimeInformation.NativeIntType)] = RuntimeInformation.TypedRefType; + binaryOpResults[CilTypePair.Create(RuntimeInformation.TypedRefType, RuntimeInformation.TypedRefType)] = RuntimeInformation.TypedRefType; + } + } +} diff --git a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/Operand.cs b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/Operand.cs index 374edeb9d18f..80b11352c441 100644 --- a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/Operand.cs +++ b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/Operand.cs @@ -20,175 +20,153 @@ /// Constant: C + [The value itself in form of string] /// PC: PC (name for this operand type is not important) /// -namespace Mono.Compiler.BigStep -{ - internal enum OperandType - { - /// The operand is a method argument. - Argument, - /// The operand is a user-defined local variable. - Local, - /// The operand is a machine-defined local variable for temporary use. - Temp, - /// The operand is a constant value stored as part of the instruction. - Const, - /// The operand is a value for the logical program counter. - PC - } - - internal interface IOperand - { - string Name { get; } - ClrType Type { get; } - OperandType OperandType { get; } - } - - abstract internal class Operand : IOperand - { - public virtual string Name { get; private set; } - public ClrType Type { get; private set; } - - internal Operand(string name, ClrType type) - { - Name = name; - Type = type; - } - - public virtual OperandType OperandType { get; } - } - - internal class BranchTargetOperand : Operand - { - public int PC { get; private set; } - - internal BranchTargetOperand(int pcvalue) - : base("PC", RuntimeInformation.VoidTypeInstance) - { - PC = pcvalue; - } - - public override OperandType OperandType => OperandType.PC; - } - - internal class ArgumentOperand : Operand - { - public int Index { get; private set; } - - internal ArgumentOperand(int index, ClrType type) - : base("A" + index, type) - { - Index = index; - } - - public override OperandType OperandType => OperandType.Argument; - } - - internal class LocalOperand : Operand - { - public int Index { get; private set; } - - internal LocalOperand(int index, ClrType type) - : base("L" + index, type) - { - Index = index; - } - - public override OperandType OperandType => OperandType.Local; - } - - internal abstract class ConstOperand : Operand - { - protected ConstOperand(ClrType type) - : base("C", type) // "C" is just a prefix. See overridden name getters in subclasses - { - } - - public override OperandType OperandType => OperandType.Const; - } - - internal class Int32ConstOperand : ConstOperand - { - public int Value { get; private set; } - - internal Int32ConstOperand(int value) - : base(RuntimeInformation.Int32TypeInstance) - { - Value = value; - } - - public override string Name { - get { - return base.Name + Value.ToString(); - } - } - } - - internal class Int64ConstOperand : ConstOperand - { - - public long Value { get; private set; } - - internal Int64ConstOperand(long value) - : base(RuntimeInformation.Int64Type) - { - Value = value; - } - - public override string Name { - get { - return base.Name + Value.ToString(); - } - } - } - - internal class Float32ConstOperand : ConstOperand - { - public float Value { get; private set; } - - internal Float32ConstOperand(float value) - : base(RuntimeInformation.Float32Type) - { - Value = value; - } - - public override string Name { - get { - return base.Name + Value.ToString(); - } - } - } - - internal class Float64ConstOperand : ConstOperand - { - - public double Value { get; private set; } - - internal Float64ConstOperand(double value) - : base(RuntimeInformation.Float64Type) - { - Value = value; - } - - public override string Name { - get { - return base.Name + Value.ToString(); - } - } - } - - internal class TempOperand : Operand - { - internal TempOperand(INameGenerator nameGen, ClrType type) - : base("T" + nameGen.NextName(), type) - { - } - - public override OperandType OperandType => OperandType.Temp; - } - - public interface INameGenerator - { - string NextName(); - } +namespace Mono.Compiler.BigStep { + internal enum OperandType { + /// The operand is a method argument. + Argument, + /// The operand is a user-defined local variable. + Local, + /// The operand is a machine-defined local variable for temporary use. + Temp, + /// The operand is a constant value stored as part of the instruction. + Const, + /// The operand is a value for the logical program counter. + PC + } + + internal interface IOperand { + string Name { get; } + ClrType Type { get; } + OperandType OperandType { get; } + } + + abstract internal class Operand : IOperand { + public virtual string Name { get; private set; } + public ClrType Type { get; private set; } + + internal Operand (string name, ClrType type) + { + Name = name; + Type = type; + } + + public virtual OperandType OperandType { get; } + } + + internal class BranchTargetOperand : Operand { + public int PC { get; private set; } + + internal BranchTargetOperand (int pcvalue) + : base ("PC", RuntimeInformation.VoidTypeInstance) + { + PC = pcvalue; + } + + public override OperandType OperandType => OperandType.PC; + } + + internal class ArgumentOperand : Operand { + public int Index { get; private set; } + + internal ArgumentOperand (int index, ClrType type) + : base ("A" + index, type) + { + Index = index; + } + + public override OperandType OperandType => OperandType.Argument; + } + + internal class LocalOperand : Operand { + public int Index { get; private set; } + + internal LocalOperand (int index, ClrType type) + : base ("L" + index, type) + { + Index = index; + } + + public override OperandType OperandType => OperandType.Local; + } + + internal abstract class ConstOperand : Operand { + protected ConstOperand (ClrType type) + : base ("C", type) // "C" is just a prefix. See overridden name getters in subclasses + { + } + + public override OperandType OperandType => OperandType.Const; + } + + internal class Int32ConstOperand : ConstOperand { + public int Value { get; private set; } + + internal Int32ConstOperand (int value) + : base (RuntimeInformation.Int32TypeInstance) + { + Value = value; + } + + public override string Name { + get { return base.Name + Value.ToString (); } + } + } + + internal class Int64ConstOperand : ConstOperand { + + public long Value { get; private set; } + + internal Int64ConstOperand (long value) + : base (RuntimeInformation.Int64Type) + { + Value = value; + } + + public override string Name { + get { return base.Name + Value.ToString (); } + } + } + + internal class Float32ConstOperand : ConstOperand { + public float Value { get; private set; } + + internal Float32ConstOperand (float value) + : base (RuntimeInformation.Float32Type) + { + Value = value; + } + + public override string Name { + get { return base.Name + Value.ToString (); } + } + } + + internal class Float64ConstOperand : ConstOperand { + + public double Value { get; private set; } + + internal Float64ConstOperand (double value) + : base (RuntimeInformation.Float64Type) + { + Value = value; + } + + public override string Name { + get { return base.Name + Value.ToString (); } + } + } + + internal class TempOperand : Operand { + internal TempOperand (INameGenerator nameGen, ClrType type) + : base ("T" + nameGen.NextName(), type) + { + } + + public override OperandType OperandType => OperandType.Temp; + } + + public interface INameGenerator { + string NextName (); + } } diff --git a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/OperationProcessor.cs b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/OperationProcessor.cs index 4b3c8955e83f..183da5f9ed68 100644 --- a/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/OperationProcessor.cs +++ b/mcs/class/Mono.Compiler/Mono.Compiler.BigStep/OperationProcessor.cs @@ -10,29 +10,29 @@ /// namespace Mono.Compiler.BigStep { - public class OperationInfo - { - /// The logical program counter for this operation. - internal int Index { get; set; } + public class OperationInfo + { + /// The logical program counter for this operation. + internal int Index { get; set; } - /// The operands may come from stack, arguments, locals or constants. - internal IOperand[] Operands { get; set; } + /// The operands may come from stack, arguments, locals or constants. + internal IOperand[] Operands { get; set; } - /// The result is pushed to the stack. - internal TempOperand Result { get; set; } + /// The result is pushed to the stack. + internal TempOperand Result { get; set; } - /// The operation. - internal Opcode Operation { get; set; } + /// The operation. + internal Opcode Operation { get; set; } - /// The extended operation. Has value only if Operation == Opcode.ExtendedPrefix - internal ExtendedOpcode? ExtOperation { get; set; } + /// The extended operation. Has value only if Operation == Opcode.ExtendedPrefix + internal ExtendedOpcode? ExtOperation { get; set; } - /// Thie operation is a jump target. - internal bool JumpTarget { get; set; } - } + /// Thie operation is a jump target. + internal bool JumpTarget { get; set; } + } - public interface IOperationProcessor - { - void Process(OperationInfo opInfo); - } -} \ No newline at end of file + public interface IOperationProcessor + { + void Process (OperationInfo opInfo); + } +} diff --git a/mcs/class/Mono.Compiler/Mono.Compiler.dll.sources b/mcs/class/Mono.Compiler/Mono.Compiler.dll.sources index 5264f4c1f5b0..5d11f205a5e3 100644 --- a/mcs/class/Mono.Compiler/Mono.Compiler.dll.sources +++ b/mcs/class/Mono.Compiler/Mono.Compiler.dll.sources @@ -13,8 +13,6 @@ Mono.Compiler/NativeCodeHandle.cs Mono.Compiler/RuntimeInformation.cs Mono.Compiler.BigStep/BigStep.cs -Mono.Compiler.BigStep/BSType.cs -Mono.Compiler.BigStep/ArgStack.cs Mono.Compiler.BigStep/CILSymbolicExecutor.cs Mono.Compiler.BigStep/OpResultTypeLookup.cs