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:
Morgan Brown 2017-10-17 13:27:08 -07:00 коммит произвёл GitHub
Родитель b97a4eee70
Коммит b9b6c5db47
5 изменённых файлов: 162 добавлений и 34 удалений

Просмотреть файл

@ -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++)