WebAssembly instance method support (#4695)
* Fix instance method parameter management and a couple of codegen issues that showed up when more methods started compiling. Includes implementing ldfld for instance fields (progress toward #4530) and the leave opcode. This is enough to make the String.Length getter work.
This commit is contained in:
Родитель
b97a4eee70
Коммит
b9b6c5db47
|
@ -125,7 +125,11 @@ namespace Internal.IL
|
|||
/// <returns>Element at the top of the stack</returns>
|
||||
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
|
|||
/// <returns>Element formerly at the top of the stack</returns>
|
||||
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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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<LLVMValueRef>(), string.Empty);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _method.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace ILCompiler
|
|||
WebAssemblyCodegenConfigProvider _config = new WebAssemblyCodegenConfigProvider(Array.Empty<string>());
|
||||
|
||||
public WebAssemblyCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group)
|
||||
: base(context, group, new CoreRTNameMangler(new WebAssemblyNodeMangler(), true))
|
||||
: base(context, group, new CoreRTNameMangler(new WebAssemblyNodeMangler(), false))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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++)
|
||||
|
|
Загрузка…
Ссылка в новой задаче