diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs index 3a6267d96..98f862c54 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs @@ -125,7 +125,11 @@ namespace Internal.IL /// Element at the top of the stack public T Peek() { - Debug.Assert(_top > 0, "Stack is not empty"); + if (_top <= 0) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return _stack[_top - 1]; } @@ -135,11 +139,11 @@ namespace Internal.IL /// Element formerly at the top of the stack public T Pop() { -#if DEBUG // This should eventually be an assert, but while many opcodes are unimplemented, we can just throw to avoid - // killing the process - if(_top <= 0) - throw new Exception("Stack is not empty"); -#endif //DEBUG + if (_top <= 0) + { + ThrowHelper.ThrowInvalidProgramException(); + } + return _stack[--_top]; } diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs index ab2f01841..7d09d7413 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs @@ -28,6 +28,8 @@ namespace Internal.IL public LLVMModuleRef Module { get; } private readonly MethodDesc _method; private readonly MethodIL _methodIL; + private readonly MethodSignature _signature; + private readonly TypeDesc _thisType; private readonly WebAssemblyCodegenCompilation _compilation; private LLVMValueRef _llvmFunction; private LLVMBasicBlockRef _curBasicBlock; @@ -78,6 +80,8 @@ namespace Internal.IL _methodIL = methodIL; _ilBytes = methodIL.GetILBytes(); _locals = methodIL.GetLocals(); + _signature = method.Signature; + _thisType = method.OwningType; var ilExceptionRegions = methodIL.GetExceptionRegions(); _exceptionRegions = new ExceptionRegion[ilExceptionRegions.Length]; @@ -300,17 +304,35 @@ namespace Internal.IL private void ImportLoadVar(int index, bool argument) { int varBase; + int varCountBase; int varOffset; LLVMTypeRef valueType; TypeDesc type; if (argument) { + varCountBase = 0; varBase = 0; - // todo: this is off by one for instance methods + if (!_signature.IsStatic) + { + varCountBase = 1; + } + GetArgSizeAndOffsetAtIndex(index, out int argSize, out varOffset); - valueType = GetLLVMTypeForTypeDesc(_method.Signature[index]); - type = _method.Signature[index]; + + if (!_signature.IsStatic && index == 0) + { + type = _thisType; + if (type.IsValueType) + { + type = type.MakeByRefType(); + } + } + else + { + type = _signature[index - varCountBase]; + } + valueType = GetLLVMTypeForTypeDesc(type); } else { @@ -489,22 +511,51 @@ namespace Internal.IL private int GetTotalParameterOffset() { int offset = 0; - for (int i = 0; i < _method.Signature.Length; i++) + for (int i = 0; i < _signature.Length; i++) { - offset += _method.Signature[i].GetElementSize().AsInt; + offset += _signature[i].GetElementSize().AsInt; } + if (!_signature.IsStatic) + { + // If this is a struct, then it's a pointer on the stack + if (_thisType.IsValueType) + { + offset += _thisType.Context.Target.PointerSize; + } + else + { + offset += _thisType.GetElementSize().AsInt; + } + } + return offset; } private void GetArgSizeAndOffsetAtIndex(int index, out int size, out int offset) { - var argType = _method.Signature[index]; + int thisSize = 0; + if (!_signature.IsStatic) + { + thisSize = _thisType.IsValueType ? _thisType.Context.Target.PointerSize : _thisType.GetElementSize().AsInt; + if (index == 0) + { + size = thisSize; + offset = 0; + return; + } + else + { + index--; + } + } + + var argType = _signature[index]; size = argType.GetElementSize().AsInt; - offset = 0; + offset = thisSize; for (int i = 0; i < index; i++) { - offset += _method.Signature[i].GetElementSize().AsInt; + offset += _signature[i].GetElementSize().AsInt; } } @@ -563,10 +614,10 @@ namespace Internal.IL private void ImportReturn() { - if(_method.Signature.ReturnType != GetWellKnownType(WellKnownType.Void)) + if (_signature.ReturnType != GetWellKnownType(WellKnownType.Void)) { StackEntry retVal = _stack.Pop(); - LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_method.Signature.ReturnType); + LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_signature.ReturnType); ImportStoreHelper(retVal.LLVMValue, valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0); } @@ -597,6 +648,11 @@ namespace Internal.IL { throw new NotImplementedException(); } + if (opcode == ILOpcode.callvirt && callee.IsAbstract) + { + throw new NotImplementedException(); + } + HandleCall(callee); } @@ -650,9 +706,15 @@ namespace Internal.IL // argument offset uint argOffset = 0; + int instanceAdjustment = 0; + if (!callee.Signature.IsStatic) + { + instanceAdjustment = 1; + } // The last argument is the top of the stack. We need to reverse them and store starting at the first argument - LLVMValueRef[] argumentValues = new LLVMValueRef[callee.Signature.Length]; + LLVMValueRef[] argumentValues = new LLVMValueRef[callee.Signature.Length + instanceAdjustment]; + for(int i = 0; i < argumentValues.Length; i++) { argumentValues[argumentValues.Length - i - 1] = _stack.Pop().LLVMValue; @@ -662,11 +724,21 @@ namespace Internal.IL { LLVMValueRef toStore = argumentValues[index]; - LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(callee.Signature[index]); + TypeDesc argType; + if (index == 0 && !callee.Signature.IsStatic) + { + argType = callee.OwningType; + } + else + { + argType = callee.Signature[index - instanceAdjustment]; + } + + LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(argType); ImportStoreHelper(toStore, valueType, castShadowStack, argOffset); - argOffset += (uint) callee.Signature[index].GetElementSize().AsInt; + argOffset += (uint)argType.GetElementSize().AsInt; } LLVM.BuildCall(_builder, fn, new LLVMValueRef[] { @@ -1250,6 +1322,21 @@ namespace Internal.IL private void ImportLoadField(int token, bool isStatic) { + if (isStatic) + { + throw new NotImplementedException("static ldfld"); + } + + FieldDesc field = (FieldDesc)_methodIL.GetObject(token); + + StackEntry objectEntry = _stack.Pop(); + + var untypedObjectPointer = LLVM.BuildPointerCast(_builder, objectEntry.LLVMValue, LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), String.Empty); + var loadLocation = LLVM.BuildGEP(_builder, untypedObjectPointer, + new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)field.Offset.AsInt, LLVMMisc.False) }, String.Empty); + var typedLoadLocation = LLVM.BuildPointerCast(_builder, loadLocation, LLVM.PointerType(GetLLVMTypeForTypeDesc(field.FieldType), 0), String.Empty); + LLVMValueRef loadValue = LLVM.BuildLoad(_builder, typedLoadLocation, "ldfld_" + field.Name); + PushExpression(GetStackValueKind(field.FieldType), "ldfld", loadValue, field.FieldType); } private void ImportAddressOfField(int token, bool isStatic) @@ -1312,6 +1399,25 @@ namespace Internal.IL private void ImportLeave(BasicBlock target) { + for (int i = 0; i < _exceptionRegions.Length; i++) + { + var r = _exceptionRegions[i]; + + if (r.ILRegion.Kind == ILExceptionRegionKind.Finally && + IsOffsetContained(_currentOffset - 1, r.ILRegion.TryOffset, r.ILRegion.TryLength) && + !IsOffsetContained(target.StartOffset, r.ILRegion.TryOffset, r.ILRegion.TryLength)) + { + MarkBasicBlock(_basicBlocks[r.ILRegion.HandlerOffset]); + } + } + + MarkBasicBlock(target); + LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(target)); + } + + private static bool IsOffsetContained(int offset, int start, int length) + { + return start <= offset && offset < start + length; } private void ImportNewArray(int token) @@ -1438,5 +1544,10 @@ namespace Internal.IL } LLVM.BuildCall(_builder, TrapFunction, Array.Empty(), string.Empty); } + + public override string ToString() + { + return _method.ToString(); + } } } diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs index 5f75f89c4..2cd878705 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs @@ -31,7 +31,8 @@ namespace Internal.IL if (method.HasCustomAttribute("System.Runtime", "RuntimeImportAttribute")) { - throw new NotImplementedException(); + methodCodeNodeNeedingCode.CompilationCompleted = true; + //throw new NotImplementedException(); //CompileExternMethod(methodCodeNodeNeedingCode, ((EcmaMethod)method).GetRuntimeImportName()); //return; } @@ -49,7 +50,18 @@ namespace Internal.IL ILImporter ilImporter = null; try { - ilImporter = new ILImporter(compilation, method, methodIL, methodCodeNodeNeedingCode.GetMangledName(compilation.NameMangler)); + string mangledName; + // TODO: We should use the startup node to generate StartupCodeMain and avoid special casing here + if (methodCodeNodeNeedingCode.Method.Signature.IsStatic && methodCodeNodeNeedingCode.Method.Name == "Main") + { + mangledName = "Main"; + } + else + { + mangledName = compilation.NameMangler.GetMangledMethodName(methodCodeNodeNeedingCode.Method).ToString(); + } + + ilImporter = new ILImporter(compilation, method, methodIL, mangledName); CompilerTypeSystemContext typeSystemContext = compilation.TypeSystemContext; diff --git a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs index 5b6919942..48bfaa15b 100644 --- a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs +++ b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs @@ -19,7 +19,7 @@ namespace ILCompiler WebAssemblyCodegenConfigProvider _config = new WebAssemblyCodegenConfigProvider(Array.Empty()); public WebAssemblyCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group) - : base(context, group, new CoreRTNameMangler(new WebAssemblyNodeMangler(), true)) + : base(context, group, new CoreRTNameMangler(new WebAssemblyNodeMangler(), false)) { } diff --git a/tests/src/Simple/HelloWasm/Program.cs b/tests/src/Simple/HelloWasm/Program.cs index 57eb439ef..63608fe6b 100644 --- a/tests/src/Simple/HelloWasm/Program.cs +++ b/tests/src/Simple/HelloWasm/Program.cs @@ -22,46 +22,47 @@ internal static class Program if(tempInt == 9) { string s = "Hello from C#!"; - PrintString(s, 14); + PrintString(s); } var not = Not(0xFFFFFFFF) == 0x00000000; if(not) { - PrintString("\n", 1); - PrintString("not test: Ok.", 13); + PrintString("\n"); + PrintString("not test: Ok."); } var negInt = Neg(42) == -42; if(negInt) { - PrintString("\n", 1); - PrintString("negInt test: Ok.", 16); + PrintString("\n"); + PrintString("negInt test: Ok."); } var shiftLeft = ShiftLeft(1, 2) == 4; if(shiftLeft) { - PrintString("\n", 1); - PrintString("shiftLeft test: Ok.", 19); + PrintString("\n"); + PrintString("shiftLeft test: Ok."); } var shiftRight = ShiftRight(4, 2) == 1; if(shiftRight) { - PrintString("\n", 1); - PrintString("shiftRight test: Ok.", 20); + PrintString("\n"); + PrintString("shiftRight test: Ok."); } var unsignedShift = UnsignedShift(0xFFFFFFFFu, 4) == 0x0FFFFFFFu; if(unsignedShift) { - PrintString("\n", 1); - PrintString("unsignedShift test: Ok.", 23); + PrintString("\n"); + PrintString("unsignedShift test: Ok."); } } - private static unsafe void PrintString(string s, int length) + private static unsafe void PrintString(string s) { + int length = s.Length; fixed (char* curChar = s) { for (int i = 0; i < length; i++)