diff options
author | Jeff Greene <hippiehunterenator@gmail.com> | 2017-11-05 02:55:48 +0300 |
---|---|---|
committer | Morgan Brown <morganbr@users.noreply.github.com> | 2017-11-05 02:55:48 +0300 |
commit | ff7decc2fdfc22c32c627734c2ff4eda2696911b (patch) | |
tree | 7736dad243601b92cc2ea72f384ebadae086cb6c /src/ILCompiler.WebAssembly | |
parent | d8d2842638c253b912e9a3917cb7d556db7189eb (diff) |
implement newobj for WASM using malloc (#4872)
implemented newobj for WASM using malloc
Diffstat (limited to 'src/ILCompiler.WebAssembly')
5 files changed, 670 insertions, 387 deletions
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs index 98f862c54..2821420a9 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs @@ -9,6 +9,7 @@ using ILCompiler.Compiler.CppCodeGen; using Internal.TypeSystem; using LLVMSharp; using ILCompiler.CodeGen; +using System.Collections.Generic; namespace Internal.IL { @@ -86,7 +87,7 @@ namespace Internal.IL /// <param name="pos">Position where to insert <paramref name="v"/></param> public void InsertAt(T v, int pos) { - Debug.Assert(pos < _top, "Invalid insertion point"); + Debug.Assert(pos <= _top, "Invalid insertion point"); if (_top >= _stack.Length) { @@ -181,18 +182,51 @@ namespace Internal.IL /// </summary> public TypeDesc Type { get; } - public LLVMValueRef LLVMValue { get; set; } + public LLVMValueRef ValueAsType(LLVMTypeRef type, LLVMBuilderRef builder) + { + return ValueAsTypeInternal(type, builder, false); + } + + public LLVMValueRef ValueAsType(TypeDesc type, LLVMBuilderRef builder) + { + return ValueAsType(ILImporter.GetLLVMTypeForTypeDesc(type), builder); + } + + public LLVMValueRef ValueForStackKind(StackValueKind kind, LLVMBuilderRef builder, bool signExtend) + { + if (kind == StackValueKind.Int32) + return ValueAsInt32(builder, signExtend); + else if (kind == StackValueKind.Int64) + return ValueAsInt64(builder, signExtend); + else if (kind == StackValueKind.Float) + return ValueAsType(LLVM.FloatType(), builder); + else if (kind == StackValueKind.NativeInt || kind == StackValueKind.ByRef || kind == StackValueKind.ObjRef) + return ValueAsInt32(builder, false); + else + throw new NotImplementedException(); + } + + public LLVMValueRef ValueAsInt32(LLVMBuilderRef builder, bool signExtend) + { + return ValueAsTypeInternal(LLVM.Int32Type(), builder, signExtend); + } + + public LLVMValueRef ValueAsInt64(LLVMBuilderRef builder, bool signExtend) + { + return ValueAsTypeInternal(LLVM.Int32Type(), builder, signExtend); + } + + protected abstract LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend); /// <summary> /// Initializes a new instance of StackEntry. /// </summary> /// <param name="kind">Kind of entry.</param> /// <param name="type">Type if any of entry.</param> - protected StackEntry(StackValueKind kind, LLVMValueRef llvmValue, TypeDesc type = null) + protected StackEntry(StackValueKind kind, TypeDesc type = null) { Kind = kind; Type = type; - LLVMValue = llvmValue; } /// <summary> @@ -206,45 +240,6 @@ namespace Internal.IL /// </summary> /// <returns>A new instance of the same type as the current entry.</returns> public abstract StackEntry Duplicate(); - - /// <summary> - /// Overridden and sealed to force descendants to override <see cref="BuildRepresentation"/>. - /// </summary> - /// <returns>String representation of current entry</returns> - public override sealed string ToString() - { - StringBuilder s = new StringBuilder(); - BuildRepresentation(s); - return s.ToString(); - } - - /// <summary> - /// Build a representation of current entry in <paramref name="s"/>. - /// </summary> - /// <param name="s">StringBuilder where representation will be saved.</param> - protected virtual void BuildRepresentation(StringBuilder s) - { - Debug.Assert(s != null, "StringBuilder is null."); - if (Type != null) - { - s.Append(Type); - if (Kind != StackValueKind.Unknown) - { - s.Append('('); - s.Append(Kind); - s.Append(')'); - } - } - else if (Kind != StackValueKind.Unknown) - { - if (Kind != StackValueKind.Unknown) - { - s.Append('('); - s.Append(Kind); - s.Append(')'); - } - } - } } /// <summary> @@ -252,7 +247,7 @@ namespace Internal.IL /// </summary> internal abstract class ConstantEntry : StackEntry { - protected ConstantEntry(StackValueKind kind, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, llvmValue, type) + protected ConstantEntry(StackValueKind kind, TypeDesc type = null) : base(kind, type) { } @@ -271,26 +266,36 @@ namespace Internal.IL { public T Value { get; } - protected ConstantEntry(StackValueKind kind, T value, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, llvmValue, type) + protected ConstantEntry(StackValueKind kind, T value, TypeDesc type = null) : base(kind, type) { Value = value; } - - protected override void BuildRepresentation(StringBuilder s) - { - base.BuildRepresentation(s); - if (s.Length > 0) - { - s.Append(' '); - } - s.Append(Value); - } } internal class Int32ConstantEntry : ConstantEntry<int> { - public Int32ConstantEntry(int value, TypeDesc type = null) : base(StackValueKind.Int32, value, LLVM.ConstInt(LLVM.Int32Type(), (ulong)value, LLVMMisc.False), type) + public Int32ConstantEntry(int value, TypeDesc type = null) : base(StackValueKind.Int32, value, type) + { + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) { + if (type.TypeKind == LLVMTypeKind.LLVMPointerTypeKind && Value == 0) + { + return LLVM.ConstPointerNull(type); + } + else if (type.TypeKind == LLVMTypeKind.LLVMPointerTypeKind && Value != 0) + { + return LLVM.ConstIntToPtr(LLVM.ConstInt(LLVM.Int32Type(), (ulong)Value, LLVMMisc.False), type); + } + else if (type.TypeKind != LLVMTypeKind.LLVMIntegerTypeKind) + { + throw new NotImplementedException(); + } + else + { + return LLVM.ConstInt(type, (ulong)Value, LLVMMisc.False); + } } public override StackEntry Duplicate() @@ -324,7 +329,7 @@ namespace Internal.IL internal class Int64ConstantEntry : ConstantEntry<long> { - public Int64ConstantEntry(long value, TypeDesc type = null) : base(StackValueKind.Int64, value, LLVM.ConstInt(LLVM.Int64Type(), (ulong)value, LLVMMisc.False), type) + public Int64ConstantEntry(long value, TypeDesc type = null) : base(StackValueKind.Int64, value, type) { } @@ -333,6 +338,26 @@ namespace Internal.IL return new Int64ConstantEntry(Value, Type); } + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + if (type.TypeKind == LLVMTypeKind.LLVMPointerTypeKind && Value == 0) + { + return LLVM.ConstPointerNull(type); + } + else if (type.TypeKind == LLVMTypeKind.LLVMPointerTypeKind && Value != 0) + { + return LLVM.ConstIntToPtr(LLVM.ConstInt(LLVM.Int64Type(), (ulong)Value, LLVMMisc.False), type); + } + else if (type.TypeKind != LLVMTypeKind.LLVMIntegerTypeKind) + { + throw new NotImplementedException(); + } + else + { + return LLVM.ConstInt(type, (ulong)Value, LLVMMisc.False); + } + } + public override bool IsCastNecessary(TypeDesc destType) { switch (destType.UnderlyingType.Category) @@ -363,10 +388,15 @@ namespace Internal.IL internal class FloatConstantEntry : ConstantEntry<double> { - public FloatConstantEntry(double value, TypeDesc type = null) : base(StackValueKind.Float, value, LLVM.ConstReal(LLVM.FloatType(), value), type) + public FloatConstantEntry(double value, TypeDesc type = null) : base(StackValueKind.Float, value, type) { } + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return LLVM.ConstReal(type, Value); + } + public override StackEntry Duplicate() { return new FloatConstantEntry(Value, Type); @@ -382,34 +412,77 @@ namespace Internal.IL /// String representation of current expression /// </summary> public string Name { get; set; } - + public LLVMValueRef RawLLVMValue { get; set; } /// <summary> /// Initializes new instance of ExpressionEntry /// </summary> /// <param name="kind">Kind of entry</param> /// <param name="name">String representation of entry</param> /// <param name="type">Type if any of entry</param> - public ExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, llvmValue, type) + public ExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, type) { Name = name; + RawLLVMValue = llvmValue; } public override StackEntry Duplicate() { - return new ExpressionEntry(Kind, Name, LLVMValue, Type); + return new ExpressionEntry(Kind, Name, RawLLVMValue, Type); } - protected override void BuildRepresentation(StringBuilder s) + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) { - base.BuildRepresentation(s); - if (s.Length > 0) - { - s.Append(' '); - } - s.Append(Name); + //TODO: deal with sign extension here + return ILImporter.CastIfNecessary(builder, RawLLVMValue, type); + } + } + + internal class LoadExpressionEntry : ExpressionEntry + { + /// <summary> + /// Initializes new instance of ExpressionEntry + /// </summary> + /// <param name="kind">Kind of entry</param> + /// <param name="name">String representation of entry</param> + /// <param name="type">Type if any of entry</param> + public LoadExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, name, llvmValue, type) + { + } + + public override StackEntry Duplicate() + { + return new LoadExpressionEntry(Kind, Name, RawLLVMValue, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return ILImporter.LoadValue(builder, RawLLVMValue, Type, type, signExtend); + } + } + + internal class AddressExpressionEntry : ExpressionEntry + { + /// <summary> + /// Initializes new instance of ExpressionEntry + /// </summary> + /// <param name="kind">Kind of entry</param> + /// <param name="name">String representation of entry</param> + /// <param name="type">Type if any of entry</param> + public AddressExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, name, llvmValue, type) + { + } + + public override StackEntry Duplicate() + { + return new LoadExpressionEntry(Kind, Name, RawLLVMValue, Type); + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return ILImporter.CastIfNecessary(builder, RawLLVMValue, type); } } - + /// <summary> /// Entry representing some token (either of TypeDesc, MethodDesc or FieldDesc) along with its string representation /// </summary> @@ -417,21 +490,19 @@ namespace Internal.IL { public T LdToken { get; } - public LdTokenEntry(StackValueKind kind, string name, T token, LLVMValueRef value, TypeDesc type = null) : base(kind, name, value, type) + public LdTokenEntry(StackValueKind kind, string name, T token, TypeDesc type = null) : base(kind, name, default(LLVMValueRef), type) { LdToken = token; } public override StackEntry Duplicate() { - return new LdTokenEntry<T>(Kind, Name, LdToken, LLVMValue, Type); + return new LdTokenEntry<T>(Kind, Name, LdToken, Type); } - protected override void BuildRepresentation(StringBuilder s) + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) { - base.BuildRepresentation(s); - s.Append(' '); - s.Append(LdToken); + return ILImporter.CastIfNecessary(builder, RawLLVMValue, type); } } @@ -442,7 +513,7 @@ namespace Internal.IL /// </summary> public static InvalidEntry Entry = new InvalidEntry(); - protected InvalidEntry() : base(StackValueKind.Unknown, default(LLVMValueRef), null) + protected InvalidEntry() : base(StackValueKind.Unknown, null) { } @@ -451,9 +522,33 @@ namespace Internal.IL return this; } - protected override void BuildRepresentation(StringBuilder s) + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + throw new InvalidOperationException(); + } + } + + /// <summary> + /// Entry representing a writable sharable stack entry that can survive from one basic block to another + /// </summary> + internal class SpilledExpressionEntry : ExpressionEntry + { + public int LocalIndex; + private ILImporter _importer; + public SpilledExpressionEntry(StackValueKind kind, string name, TypeDesc type, int localIndex, ILImporter importer) : base(kind, name, new LLVMValueRef(IntPtr.Zero), type) + { + LocalIndex = localIndex; + _importer = importer; + } + + protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + { + return _importer.LoadTemp(LocalIndex, type); + } + + public override StackEntry Duplicate() { - s.Append("Invalid Entry"); + return new SpilledExpressionEntry(Kind, Name, Type, LocalIndex, _importer); } } } diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs index 8c7886288..ebe4a44bf 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs @@ -19,6 +19,13 @@ namespace Internal.IL // backend before the actual compilation happens to gain insights into the code. partial class ILImporter { + public enum LocalVarKind + { + Argument, + Local, + Temp + } + ArrayBuilder<object> _dependencies = new ArrayBuilder<object>(); public IEnumerable<object> GetDependencies() { @@ -35,6 +42,7 @@ namespace Internal.IL private LLVMBasicBlockRef _curBasicBlock; private LLVMBuilderRef _builder; private readonly LocalVariableDefinition[] _locals; + private List<SpilledExpressionEntry> _spilledExpressions = new List<SpilledExpressionEntry>(); private readonly byte[] _ilBytes; @@ -154,6 +162,30 @@ namespace Internal.IL return llvmFunction; } + private LLVMValueRef GetOrCreateLLVMFunction(string mangledName, LLVMTypeRef functionType) + { + LLVMValueRef llvmFunction = LLVM.GetNamedFunction(Module, mangledName); + + if (llvmFunction.Pointer == IntPtr.Zero) + { + return LLVM.AddFunction(Module, mangledName, functionType); + } + return llvmFunction; + } + + private void ImportCallMemset(LLVMValueRef targetPointer, byte value, int length) + { + LLVMValueRef objectSizeValue = BuildConstInt32(length); + var memsetSignature = LLVM.FunctionType(LLVM.VoidType(), new LLVMTypeRef[] { LLVM.PointerType(LLVM.Int8Type(), 0), LLVM.Int8Type(), LLVM.Int32Type(), LLVM.Int32Type(), LLVM.Int1Type() }, false); + LLVM.BuildCall(_builder, GetOrCreateLLVMFunction("llvm.memset.p0i8.i32", memsetSignature), new LLVMValueRef[] { targetPointer, BuildConstInt8(value), objectSizeValue, BuildConstInt32(1), BuildConstInt1(0) }, String.Empty); + } + + private void PushLoadExpression(StackValueKind kind, string name, LLVMValueRef rawLLVMValue, TypeDesc type) + { + Debug.Assert(kind != StackValueKind.Unknown, "Unknown stack kind"); + _stack.Push(new LoadExpressionEntry(kind, name, rawLLVMValue, type)); + } + /// <summary> /// Push an expression named <paramref name="name"/> of kind <paramref name="kind"/>. /// </summary> @@ -194,45 +226,6 @@ namespace Internal.IL _stack.Push(new ExpressionEntry(kind, name, llvmValue, type)); } - - - /// <summary> - /// Generate a cast in case the stack type of source is not identical or compatible with destination type. - /// </summary> - /// <param name="destType">Type of destination</param> - /// <param name="srcEntry">Source entry from stack</param> - private void AppendCastIfNecessary(TypeDesc destType, StackEntry srcEntry) - { - ConstantEntry constant = srcEntry as ConstantEntry; - if ((constant != null) && (constant.IsCastNecessary(destType)) || !destType.IsValueType || destType != srcEntry.Type) - { - throw new NotImplementedException(); - /* - Append("("); - Append(GetSignatureTypeNameAndAddReference(destType)); - Append(")");*/ - } - } - - private void AppendCastIfNecessary(StackValueKind dstType, TypeDesc srcType) - { - if (dstType == StackValueKind.ByRef) - { - - throw new NotImplementedException(); - /* - Append("("); - Append(GetSignatureTypeNameAndAddReference(srcType)); - Append(")");*/ - } - else - if (srcType.IsPointer) - { - throw new NotImplementedException(); - //Append("(intptr_t)"); - } - } - private void MarkInstructionBoundary() { @@ -281,6 +274,8 @@ namespace Internal.IL var terminator = basicBlock.Block.GetBasicBlockTerminator(); if (terminator.Pointer == IntPtr.Zero) { + if (_basicBlocks[_currentOffset].StartOffset == 0) + throw new InvalidProgramException(); LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(_basicBlocks[_currentOffset])); } } @@ -304,13 +299,72 @@ namespace Internal.IL private void ImportLoadVar(int index, bool argument) { + LLVMValueRef typedLoadLocation = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out TypeDesc type); + PushLoadExpression(GetStackValueKind(type), "ld" + (argument ? "arg" : "loc") + index + "_", typedLoadLocation, type); + } + + private LLVMValueRef LoadTemp(int index) + { + LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); + return LLVM.BuildLoad(_builder, CastToPointerToTypeDesc(address, type), "ldtemp"); + } + + internal LLVMValueRef LoadTemp(int index, LLVMTypeRef asType) + { + LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); + return LLVM.BuildLoad(_builder, CastIfNecessary(address, LLVM.PointerType(asType, 0)), "ldtemp"); + } + + private void StoreTemp(int index, LLVMValueRef value) + { + LLVMValueRef address = LoadVarAddress(index, LocalVarKind.Temp, out TypeDesc type); + LLVM.BuildStore(_builder, CastToTypeDesc(value, type), CastToPointerToTypeDesc(address, type)); + } + + internal static LLVMValueRef LoadValue(LLVMBuilderRef builder, LLVMValueRef address, TypeDesc sourceType, LLVMTypeRef targetType, bool signExtend) + { + if (targetType.TypeKind == LLVMTypeKind.LLVMIntegerTypeKind && sourceType.IsPrimitive && !sourceType.IsPointer) + { + var sourceLLVMType = ILImporter.GetLLVMTypeForTypeDesc(sourceType); + var typedAddress = CastIfNecessary(builder, address, LLVM.PointerType(sourceLLVMType, 0)); + return CastIntValue(builder, LLVM.BuildLoad(builder, typedAddress, "ldvalue"), targetType, signExtend); + } + else + { + var typedAddress = CastIfNecessary(builder, address, LLVM.PointerType(targetType, 0)); + return LLVM.BuildLoad(builder, typedAddress, "ldvalue"); + } + } + + private static LLVMValueRef CastIntValue(LLVMBuilderRef builder, LLVMValueRef value, LLVMTypeRef type, bool signExtend) + { + if (LLVM.TypeOf(value).Pointer == type.Pointer) + { + return value; + } + else if (LLVM.TypeOf(value).TypeKind == LLVMTypeKind.LLVMPointerTypeKind) + { + return LLVM.BuildPtrToInt(builder, value, type, "intcast"); + } + else if (signExtend && type.GetIntTypeWidth() > LLVM.TypeOf(value).GetIntTypeWidth()) + { + return LLVM.BuildSExtOrBitCast(builder, value, type, "SExtOrBitCast"); + } + else + { + Debug.Assert(LLVM.TypeOf(value).TypeKind == LLVMTypeKind.LLVMIntegerTypeKind); + return LLVM.BuildIntCast(builder, value, type, "intcast"); + } + } + + private LLVMValueRef LoadVarAddress(int index, LocalVarKind kind, out TypeDesc type) + { int varBase; int varCountBase; int varOffset; LLVMTypeRef valueType; - TypeDesc type; - if (argument) + if (kind == LocalVarKind.Argument) { varCountBase = 0; varBase = 0; @@ -335,21 +389,25 @@ namespace Internal.IL } valueType = GetLLVMTypeForTypeDesc(type); } - else + else if (kind == LocalVarKind.Local) { varBase = GetTotalParameterOffset(); GetLocalSizeAndOffsetAtIndex(index, out int localSize, out varOffset); valueType = GetLLVMTypeForTypeDesc(_locals[index].Type); type = _locals[index].Type; } + else + { + varBase = GetTotalRealLocalOffset(); + GetSpillSizeAndOffsetAtIndex(index, out int localSize, out varOffset); + valueType = GetLLVMTypeForTypeDesc(_spilledExpressions[index].Type); + type = _spilledExpressions[index].Type; + } - var loadLocation = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), + return LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)(varBase + varOffset), LLVMMisc.False) }, String.Empty); - var typedLoadLocation = LLVM.BuildPointerCast(_builder, loadLocation, LLVM.PointerType(valueType, 0), "typedLoadLocation"); - var loadResult = LLVM.BuildLoad(_builder, typedLoadLocation, "ld" + (argument ? "arg" : "loc") + index + "_"); - PushExpression(GetStackValueKind(type), String.Empty, loadResult, type); } private StackValueKind GetStackValueKind(TypeDesc type) @@ -395,24 +453,16 @@ namespace Internal.IL private void ImportStoreVar(int index, bool argument) { - if(argument) - { - throw new NotImplementedException("storing to argument"); - } - - GetLocalSizeAndOffsetAtIndex(index, out int localSize, out int localOffset); - - LLVMValueRef toStore = _stack.Pop().LLVMValue; - - LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_locals[index].Type); - - ImportStoreHelper(toStore, valueType, LLVM.GetFirstParam(_llvmFunction), (uint)(GetTotalParameterOffset() + localOffset)); + TypeDesc varType; + StackEntry toStore = _stack.Pop(); + LLVMValueRef varAddress = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out varType); + CastingStore(varAddress, toStore, varType); } private void ImportStoreHelper(LLVMValueRef toStore, LLVMTypeRef valueType, LLVMValueRef basePtr, uint offset) { LLVMValueRef typedToStore = CastIfNecessary(toStore, valueType); - + var storeLocation = LLVM.BuildGEP(_builder, basePtr, new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), offset, LLVMMisc.False) }, String.Empty); @@ -420,8 +470,34 @@ namespace Internal.IL LLVM.BuildStore(_builder, typedToStore, typedStoreLocation); } + private LLVMValueRef CastToRawPointer(LLVMValueRef source) + { + return CastIfNecessary(source, LLVM.PointerType(LLVM.Int8Type(), 0)); + } + + private LLVMValueRef CastToTypeDesc(LLVMValueRef source, TypeDesc type) + { + return CastIfNecessary(source, GetLLVMTypeForTypeDesc(type)); + } + + private LLVMValueRef CastToPointerToTypeDesc(LLVMValueRef source, TypeDesc type) + { + return CastIfNecessary(source, LLVM.PointerType(GetLLVMTypeForTypeDesc(type), 0)); + } + + private void CastingStore(LLVMValueRef address, StackEntry value, TypeDesc targetType) + { + var typedStoreLocation = CastToPointerToTypeDesc(address, targetType); + LLVM.BuildStore(_builder, value.ValueAsType(targetType, _builder), typedStoreLocation); + } + private LLVMValueRef CastIfNecessary(LLVMValueRef source, LLVMTypeRef valueType) { + return CastIfNecessary(_builder, source, valueType); + } + + internal static LLVMValueRef CastIfNecessary(LLVMBuilderRef builder, LLVMValueRef source, LLVMTypeRef valueType) + { LLVMTypeRef sourceType = LLVM.TypeOf(source); if (sourceType.Pointer == valueType.Pointer) return source; @@ -432,11 +508,19 @@ namespace Internal.IL LLVMValueRef typedToStore = source; if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) { - typedToStore = LLVM.BuildPointerCast(_builder, source, valueType, "CastIfNecessaryPtr"); + typedToStore = LLVM.BuildPointerCast(builder, source, valueType, "CastIfNecessaryPtr"); } else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMIntegerTypeKind) { - typedToStore = LLVM.BuildPtrToInt(_builder, source, valueType, "CastIfNecessaryInt"); + typedToStore = LLVM.BuildPtrToInt(builder, source, valueType, "CastIfNecessaryInt"); + } + else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMArrayTypeKind) + { + typedToStore = LLVM.BuildLoad(builder, CastIfNecessary(builder, source, LLVM.PointerType(valueType, 0)), "CastIfNecessaryArrayLoad"); + } + else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind == LLVMTypeKind.LLVMArrayTypeKind) + { + typedToStore = LLVM.BuildLoad(builder, CastIfNecessary(builder, source, LLVM.PointerType(valueType, 0)), "CastIfNecessaryArrayLoad"); } else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind != LLVMTypeKind.LLVMIntegerTypeKind) { @@ -444,7 +528,7 @@ namespace Internal.IL } else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) { - typedToStore = LLVM.BuildIntToPtr(_builder, source, valueType, "CastIfNecessaryPtr"); + typedToStore = LLVM.BuildIntToPtr(builder, source, valueType, "CastIfNecessaryPtr"); } else if (toStoreKind != LLVMTypeKind.LLVMIntegerTypeKind && valueTypeKind == LLVMTypeKind.LLVMPointerTypeKind) { @@ -457,13 +541,21 @@ namespace Internal.IL else if (toStoreKind == valueTypeKind && toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind) { Debug.Assert(toStoreKind != LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind != LLVMTypeKind.LLVMPointerTypeKind); - typedToStore = LLVM.BuildIntCast(_builder, source, valueType, "CastIfNecessaryInt"); + typedToStore = LLVM.BuildIntCast(builder, source, valueType, "CastIfNecessaryInt"); + } + else if (toStoreKind == LLVMTypeKind.LLVMFloatTypeKind && valueTypeKind != LLVMTypeKind.LLVMFloatTypeKind) + { + typedToStore = LLVM.BuildIntCast(builder, source, valueType, "CastIfNecessaryFloat"); + } + else if (toStoreKind != LLVMTypeKind.LLVMFloatTypeKind && valueTypeKind == LLVMTypeKind.LLVMFloatTypeKind) + { + typedToStore = LLVM.BuildFPCast(builder, source, valueType, "CastIfNecessaryFloat"); } return typedToStore; } - private LLVMTypeRef GetLLVMTypeForTypeDesc(TypeDesc type) + internal static LLVMTypeRef GetLLVMTypeForTypeDesc(TypeDesc type) { switch (type.Category) { @@ -481,9 +573,9 @@ namespace Internal.IL case TypeFlags.Int32: case TypeFlags.UInt32: + return LLVM.Int32Type(); case TypeFlags.IntPtr: case TypeFlags.UIntPtr: - return LLVM.Int32Type(); case TypeFlags.Array: case TypeFlags.SzArray: case TypeFlags.ByRef: @@ -492,7 +584,7 @@ namespace Internal.IL return LLVM.PointerType(LLVM.Int8Type(), 0); case TypeFlags.Pointer: - return LLVM.PointerType(GetLLVMTypeForTypeDesc(type.GetParameterType()), 0); + return LLVM.PointerType(type.GetParameterType().IsVoid ? LLVM.Int8Type() : GetLLVMTypeForTypeDesc(type.GetParameterType()), 0); case TypeFlags.Int64: case TypeFlags.UInt64: @@ -521,6 +613,16 @@ namespace Internal.IL private int GetTotalLocalOffset() { + int offset = GetTotalRealLocalOffset(); + for (int i = 0; i < _spilledExpressions.Count; i++) + { + offset += _spilledExpressions[i].Type.GetElementSize().AsInt; + } + return offset; + } + + private int GetTotalRealLocalOffset() + { int offset = 0; for (int i = 0; i < _locals.Length; i++) { @@ -592,22 +694,23 @@ namespace Internal.IL } } - private void ImportAddressOfVar(int index, bool argument) + private void GetSpillSizeAndOffsetAtIndex(int index, out int size, out int offset) { - if (argument) + SpilledExpressionEntry spill = _spilledExpressions[index]; + size = spill.Type.GetElementSize().AsInt; + + offset = 0; + for (int i = 0; i < index; i++) { - throw new NotImplementedException("ldarga"); + offset += _spilledExpressions[i].Type.GetElementSize().AsInt; } + } - int localOffset = GetTotalParameterOffset(); - GetLocalSizeAndOffsetAtIndex(index, out int size, out int offset); - localOffset += offset; - - var localPtr = LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), - new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (uint)localOffset, LLVMMisc.False) }, "ldloca"); - //var typedLocalPtr = LLVM.BuildPointerCast(_builder, localPtr, GetLLVMTypeForTypeDesc(_locals[index].Type.MakePointerType()), "ldloca"); - - _stack.Push(new ExpressionEntry(StackValueKind.ByRef, "ldloca", localPtr, _locals[index].Type.MakePointerType())); + private void ImportAddressOfVar(int index, bool argument) + { + TypeDesc type; + LLVMValueRef typedLoadLocation = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out type); + _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldloca", typedLoadLocation, type.MakePointerType())); } private void ImportDup() @@ -640,8 +743,7 @@ namespace Internal.IL { StackEntry retVal = _stack.Pop(); LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_signature.ReturnType); - - ImportStoreHelper(retVal.LLVMValue, valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0); + ImportStoreHelper(retVal.ValueAsType(valueType, _builder), valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0); } LLVM.BuildRetVoid(_builder); @@ -650,7 +752,6 @@ namespace Internal.IL private void ImportCall(ILOpcode opcode, int token) { MethodDesc callee = (MethodDesc)_methodIL.GetObject(token); - if (callee.IsIntrinsic) { if (ImportIntrinsicCall(callee)) @@ -665,8 +766,29 @@ namespace Internal.IL return; } + if (opcode == ILOpcode.newobj) + { + if (callee.OwningType.IsString) + { + // String constructors actually look like regular method calls + IMethodNode node = _compilation.NodeFactory.StringAllocator(callee); + _dependencies.Add(node); + callee = node.Method; + opcode = ILOpcode.call; + } + else + { + StackEntry newObjResult = AllocateObject(callee.OwningType); + //one for the real result and one to be consumed by ctor + if (callee.Signature.Length > _stack.Length) //System.Reflection.MemberFilter.ctor + throw new InvalidProgramException(); + _stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length); + _stack.InsertAt(newObjResult, _stack.Top - callee.Signature.Length); + } + } + // we don't really have virtual call support, but we'll treat it as direct for now - if (opcode != ILOpcode.call && opcode != ILOpcode.callvirt) + if (opcode != ILOpcode.call && opcode != ILOpcode.callvirt && opcode != ILOpcode.newobj) { throw new NotImplementedException(); } @@ -678,6 +800,45 @@ namespace Internal.IL HandleCall(callee); } + private ExpressionEntry AllocateObject(TypeDesc type) + { + MetadataType metadataType = (MetadataType)type; + int objectSize = metadataType.InstanceByteCount.AsInt; + LLVMValueRef allocatedMemory = LLVM.BuildMalloc(_builder, LLVM.ArrayType(LLVM.Int8Type(), (uint)objectSize), "newobj"); + LLVMValueRef castMemory = LLVM.BuildPointerCast(_builder, allocatedMemory, LLVM.PointerType(LLVM.Int8Type(), 0), "castnewobj"); + ImportCallMemset(castMemory, 0, objectSize); + LLVMValueRef eeTypePointer = GetEETypeForTypeDesc(type); + LLVMValueRef objectHeaderPtr = LLVM.BuildPointerCast(_builder, allocatedMemory, LLVM.PointerType(LLVM.TypeOf(eeTypePointer), 0), "objectHeaderPtr"); + LLVM.BuildStore(_builder, eeTypePointer, objectHeaderPtr); + return new ExpressionEntry(StackValueKind.ObjRef, "newobj", castMemory, type); + } + + private static LLVMValueRef BuildConstInt1(int number) + { + Debug.Assert(number == 0 || number == 1, "Non-boolean int1"); + return LLVM.ConstInt(LLVM.Int1Type(), (ulong)number, LLVMMisc.False); + } + + private static LLVMValueRef BuildConstInt8(byte number) + { + return LLVM.ConstInt(LLVM.Int8Type(), number, LLVMMisc.False); + } + + private static LLVMValueRef BuildConstInt32(int number) + { + return LLVM.ConstInt(LLVM.Int32Type(), (ulong)number, LLVMMisc.False); + } + + private LLVMValueRef GetEETypeForTypeDesc(TypeDesc target) + { + ISymbolNode node = _compilation.NodeFactory.ConstructedTypeSymbol(target); + LLVMValueRef eeTypePointer = LoadAddressOfSymbolNode(node); + _dependencies.Add(node); + var eeTypePtrType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr"); + var ptrPtrType = LLVM.PointerType(GetLLVMTypeForTypeDesc(eeTypePtrType), 0); + return LLVM.BuildPointerCast(_builder, eeTypePointer, ptrPtrType, "castEETypePtr"); + } + /// <summary> /// Implements intrinsic methods instread of calling them /// </summary> @@ -735,16 +896,16 @@ namespace Internal.IL } // 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 + instanceAdjustment]; + StackEntry[] argumentValues = new StackEntry[callee.Signature.Length + instanceAdjustment]; for(int i = 0; i < argumentValues.Length; i++) { - argumentValues[argumentValues.Length - i - 1] = _stack.Pop().LLVMValue; + argumentValues[argumentValues.Length - i - 1] = _stack.Pop(); } for (int index = 0; index < argumentValues.Length; index++) { - LLVMValueRef toStore = argumentValues[index]; + StackEntry toStore = argumentValues[index]; TypeDesc argType; if (index == 0 && !callee.Signature.IsStatic) @@ -758,7 +919,7 @@ namespace Internal.IL LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(argType); - ImportStoreHelper(toStore, valueType, castShadowStack, argOffset); + ImportStoreHelper(toStore.ValueAsType(valueType, _builder), valueType, castShadowStack, argOffset); argOffset += (uint)argType.GetElementSize().AsInt; } @@ -772,8 +933,7 @@ namespace Internal.IL { LLVMTypeRef returnLLVMType = GetLLVMTypeForTypeDesc(callee.Signature.ReturnType); LLVMValueRef returnLLVMPointer = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(returnLLVMType, 0), "castreturnpointer"); - LLVMValueRef loadResult = LLVM.BuildLoad(_builder, returnLLVMPointer, String.Empty); - PushExpression(GetStackValueKind(callee.Signature.ReturnType), String.Empty, loadResult, callee.Signature.ReturnType); + PushLoadExpression(GetStackValueKind(callee.Signature.ReturnType), String.Empty, returnLLVMPointer, callee.Signature.ReturnType); } } @@ -786,6 +946,11 @@ namespace Internal.IL { LLVMValueRef nativeFunc = LLVM.GetNamedFunction(Module, method.Name); + //emscripten dies if this is output because its expected to have i32, i32, i64. But the runtime has defined it as i8*, i8*, i64 + if (method.Name == "memmove") + throw new NotImplementedException(); + + // Create an import if we haven't already if (nativeFunc.Pointer == IntPtr.Zero) { @@ -806,24 +971,17 @@ namespace Internal.IL LLVMValueRef[] arguments = new LLVMValueRef[method.Signature.Length]; for(int i = 0; i < arguments.Length; i++) { - LLVMValueRef argValue = _stack.Pop().LLVMValue; - // Arguments are reversed on the stack // Coerce pointers to the native type TypeDesc signatureType = method.Signature[arguments.Length - i - 1]; - LLVMValueRef typedValue = argValue; - if (signatureType.IsPointer) - { - LLVMTypeRef signatureLlvmType = GetLLVMTypeForTypeDesc(signatureType); - typedValue = LLVM.BuildPointerCast(_builder, argValue, signatureLlvmType, "castarg"); - } - arguments[arguments.Length - i - 1] = typedValue; + arguments[arguments.Length - i - 1] = _stack.Pop().ValueAsType(GetLLVMTypeForTypeDesc(signatureType), _builder); } - var returnValue = LLVM.BuildCall(_builder, nativeFunc, arguments, "call"); + //dont name the return value if the function returns void, its invalid + var returnValue = LLVM.BuildCall(_builder, nativeFunc, arguments, !method.Signature.ReturnType.IsVoid ? "call" : string.Empty); if(!method.Signature.ReturnType.IsVoid) - PushExpression(GetStackValueKind(method.Signature.ReturnType), String.Empty, returnValue, method.Signature.ReturnType); + PushExpression(GetStackValueKind(method.Signature.ReturnType), "retval", returnValue, method.Signature.ReturnType); } private void ImportCalli(int token) @@ -862,6 +1020,10 @@ namespace Internal.IL { if (opcode == ILOpcode.br) { + ImportFallthrough(target); + //TODO: why does this illegal branch happen in System.Reflection.MemberFilter.ctor + if (target.StartOffset == 0) + throw new InvalidProgramException(); LLVM.BuildBr(_builder, GetLLVMBasicBlockForBlock(target)); } else @@ -871,11 +1033,10 @@ namespace Internal.IL if (opcode == ILOpcode.brfalse || opcode == ILOpcode.brtrue) { var op = _stack.Pop(); - LLVMValueRef value = op.LLVMValue; - if (LLVM.GetTypeKind(LLVM.TypeOf(value)) == LLVMTypeKind.LLVMPointerTypeKind) - { - value = LLVM.BuildPtrToInt(_builder, value, LLVM.Int32Type(), String.Empty); - } + LLVMValueRef value = op.ValueAsInt32(_builder, false); + + if (LLVM.TypeOf(value).TypeKind != LLVMTypeKind.LLVMIntegerTypeKind) + throw new InvalidProgramException("branch on non integer"); if (opcode == ILOpcode.brfalse) { @@ -903,21 +1064,8 @@ namespace Internal.IL kind = op2.Kind; } - LLVMValueRef left = op1.LLVMValue; - LLVMValueRef right = op2.LLVMValue; - - if (kind == StackValueKind.NativeInt || kind == StackValueKind.ObjRef || kind == StackValueKind.ByRef) - { - if (LLVM.GetTypeKind(LLVM.TypeOf(left)) == LLVMTypeKind.LLVMPointerTypeKind) - { - left = LLVM.BuildPtrToInt(_builder, left, LLVM.Int32Type(), "lptrasint"); - } - if (LLVM.GetTypeKind(LLVM.TypeOf(right)) == LLVMTypeKind.LLVMPointerTypeKind) - { - right = LLVM.BuildPtrToInt(_builder, right, LLVM.Int32Type(), "rptrasint"); - } - } - + LLVMValueRef left = op1.ValueForStackKind(kind, _builder, false); + LLVMValueRef right = op2.ValueForStackKind(kind, _builder, false); switch (opcode) { @@ -959,19 +1107,17 @@ namespace Internal.IL if (target.StartOffset == 0) throw new NotImplementedException("cant branch to entry basic block"); - LLVM.BuildCondBr(_builder, condition, GetLLVMBasicBlockForBlock(target), GetLLVMBasicBlockForBlock(fallthrough)); - + ImportFallthrough(target); ImportFallthrough(fallthrough); + LLVM.BuildCondBr(_builder, condition, GetLLVMBasicBlockForBlock(target), GetLLVMBasicBlockForBlock(fallthrough)); } - - ImportFallthrough(target); } private void ImportSwitchJump(int jmpBase, int[] jmpDelta, BasicBlock fallthrough) { var operand = _stack.Pop(); - var @switch = LLVM.BuildSwitch(_builder, operand.LLVMValue, GetLLVMBasicBlockForBlock(fallthrough), (uint)jmpDelta.Length); + var @switch = LLVM.BuildSwitch(_builder, operand.ValueAsInt32(_builder, false), GetLLVMBasicBlockForBlock(fallthrough), (uint)jmpDelta.Length); for (var i = 0; i < jmpDelta.Length; i++) { var target = _basicBlocks[_currentOffset + jmpDelta[i]]; @@ -989,22 +1135,13 @@ namespace Internal.IL private void ImportLoadIndirect(TypeDesc type) { - StackEntry pointer = _stack.Pop(); - LLVMTypeRef loadType = GetLLVMTypeForTypeDesc(type); - LLVMTypeRef pointerType = LLVM.PointerType(loadType, 0); - - LLVMValueRef typedPointer; - if (LLVM.GetTypeKind(LLVM.TypeOf(pointer.LLVMValue)) != LLVMTypeKind.LLVMPointerTypeKind) - { - typedPointer = LLVM.BuildIntToPtr(_builder, pointer.LLVMValue, pointerType, "ldindintptrcast"); - } - else - { - typedPointer = LLVM.BuildPointerCast(_builder, pointer.LLVMValue, pointerType, "ldindptrcast"); - } - - LLVMValueRef load = LLVM.BuildLoad(_builder, typedPointer, "ldind"); - PushExpression(GetStackValueKind(type), "ldlind", load, type); + var pointer = _stack.Pop(); + Debug.Assert(pointer is ExpressionEntry || pointer is ConstantEntry); + var expressionPointer = pointer as ExpressionEntry; + TypeDesc pointerElementType = pointer.Type.GetParameterType(); + LLVMValueRef rawValue = expressionPointer?.RawLLVMValue ?? LLVM.ConstNull(GetLLVMTypeForTypeDesc(pointerElementType)); + _stack.Push(new LoadExpressionEntry(type != null ? GetStackValueKind(type) : StackValueKind.ByRef, "ldind", + rawValue, pointer.Type.GetParameterType())); } private void ImportStoreIndirect(int token) @@ -1016,29 +1153,18 @@ namespace Internal.IL { StackEntry value = _stack.Pop(); StackEntry destinationPointer = _stack.Pop(); - LLVMTypeRef requestedPointerType = LLVM.PointerType(GetLLVMTypeForTypeDesc(type), 0); - LLVMValueRef typedValue = value.LLVMValue; - LLVMValueRef typedPointer = destinationPointer.LLVMValue; + LLVMValueRef typedValue; + LLVMValueRef typedPointer; - if (LLVM.GetTypeKind(LLVM.TypeOf(destinationPointer.LLVMValue)) != LLVMTypeKind.LLVMPointerTypeKind) + if (type != null) { - typedPointer = LLVM.BuildIntToPtr(_builder, destinationPointer.LLVMValue, requestedPointerType, "stindintptrcast"); + typedValue = value.ValueAsType(type, _builder); + typedPointer = destinationPointer.ValueAsType(type.MakePointerType(), _builder); } else { - typedPointer = LLVM.BuildPointerCast(_builder, destinationPointer.LLVMValue, requestedPointerType, "stindptrcast"); - } - - if (value.Type != type) - { - if (LLVM.GetTypeKind(GetLLVMTypeForTypeDesc(value.Type)) != LLVMTypeKind.LLVMPointerTypeKind) - { - typedValue = LLVM.BuildIntCast(_builder, typedValue, GetLLVMTypeForTypeDesc(type), "stindvalcast"); - } - else - { - typedValue = LLVM.BuildPointerCast(_builder, typedValue, GetLLVMTypeForTypeDesc(type), "stindvalptrcast"); - } + typedPointer = destinationPointer.ValueAsType(LLVM.PointerType(LLVM.Int32Type(), 0), _builder); + typedValue = value.ValueAsInt32(_builder, false); } LLVM.BuildStore(_builder, typedValue, typedPointer); @@ -1065,106 +1191,140 @@ namespace Internal.IL } // The one exception from the above rule - if ((kind == StackValueKind.ByRef) && - (opcode == ILOpcode.sub || opcode == ILOpcode.sub_ovf || opcode == ILOpcode.sub_ovf_un)) + if (kind == StackValueKind.ByRef) { kind = StackValueKind.NativeInt; - type = null; + type = type.MakePointerType(); } LLVMValueRef result; - LLVMValueRef left = op1.LLVMValue; - LLVMValueRef right = op2.LLVMValue; - - if (kind == StackValueKind.NativeInt || kind == StackValueKind.ObjRef || kind == StackValueKind.ByRef) + LLVMValueRef left = op1.ValueForStackKind(kind, _builder, false); + LLVMValueRef right = op2.ValueForStackKind(kind, _builder, false); + if (kind == StackValueKind.Float) { - if (LLVM.GetTypeKind(LLVM.TypeOf(left)) == LLVMTypeKind.LLVMPointerTypeKind) + switch (opcode) { - left = LLVM.BuildPtrToInt(_builder, left, LLVM.Int32Type(), "lptrasint"); + case ILOpcode.add: + result = LLVM.BuildFAdd(_builder, left, right, "fadd"); + break; + case ILOpcode.sub: + result = LLVM.BuildFSub(_builder, left, right, "fsub"); + break; + case ILOpcode.mul: + result = LLVM.BuildFMul(_builder, left, right, "fmul"); + break; + case ILOpcode.div: + result = LLVM.BuildFDiv(_builder, left, right, "fdiv"); + break; + case ILOpcode.rem: + result = LLVM.BuildFRem(_builder, left, right, "frem"); + break; + + // TODO: Overflow checks + case ILOpcode.add_ovf: + case ILOpcode.add_ovf_un: + result = LLVM.BuildFAdd(_builder, left, right, "fadd"); + break; + case ILOpcode.sub_ovf: + case ILOpcode.sub_ovf_un: + result = LLVM.BuildFSub(_builder, left, right, "fsub"); + break; + case ILOpcode.mul_ovf: + case ILOpcode.mul_ovf_un: + result = LLVM.BuildFMul(_builder, left, right, "fmul"); + break; + + default: + throw new InvalidOperationException(); // Should be unreachable } - if (LLVM.GetTypeKind(LLVM.TypeOf(right)) == LLVMTypeKind.LLVMPointerTypeKind) + } + else + { + switch (opcode) { - right = LLVM.BuildPtrToInt(_builder, right, LLVM.Int32Type(), "rptrasint"); + case ILOpcode.add: + result = LLVM.BuildAdd(_builder, left, right, "add"); + break; + case ILOpcode.sub: + result = LLVM.BuildSub(_builder, left, right, "sub"); + break; + case ILOpcode.mul: + result = LLVM.BuildMul(_builder, left, right, "mul"); + break; + case ILOpcode.div: + result = LLVM.BuildSDiv(_builder, left, right, "sdiv"); + break; + case ILOpcode.div_un: + result = LLVM.BuildUDiv(_builder, left, right, "udiv"); + break; + case ILOpcode.rem: + result = LLVM.BuildSRem(_builder, left, right, "srem"); + break; + case ILOpcode.rem_un: + result = LLVM.BuildURem(_builder, left, right, "urem"); + break; + case ILOpcode.and: + result = LLVM.BuildAnd(_builder, left, right, "and"); + break; + case ILOpcode.or: + result = LLVM.BuildOr(_builder, left, right, "or"); + break; + case ILOpcode.xor: + result = LLVM.BuildXor(_builder, left, right, "xor"); + break; + + // TODO: Overflow checks + case ILOpcode.add_ovf: + case ILOpcode.add_ovf_un: + result = LLVM.BuildAdd(_builder, left, right, "add"); + break; + case ILOpcode.sub_ovf: + case ILOpcode.sub_ovf_un: + result = LLVM.BuildSub(_builder, left, right, "sub"); + break; + case ILOpcode.mul_ovf: + case ILOpcode.mul_ovf_un: + result = LLVM.BuildMul(_builder, left, right, "mul"); + break; + + default: + throw new InvalidOperationException(); // Should be unreachable } } - switch (opcode) - { - case ILOpcode.add: - result = LLVM.BuildAdd(_builder, left, right, "add"); - break; - case ILOpcode.sub: - result = LLVM.BuildSub(_builder, left, right, "sub"); - break; - case ILOpcode.mul: - result = LLVM.BuildMul(_builder, left, right, "mul"); - break; - case ILOpcode.div: - result = LLVM.BuildSDiv(_builder, left, right, "sdiv"); - break; - case ILOpcode.div_un: - result = LLVM.BuildUDiv(_builder, left, right, "udiv"); - break; - case ILOpcode.rem: - result = LLVM.BuildSRem(_builder, left, right, "srem"); - break; - case ILOpcode.rem_un: - result = LLVM.BuildURem(_builder, left, right, "urem"); - break; - case ILOpcode.and: - result = LLVM.BuildAnd(_builder, left, right, "and"); - break; - case ILOpcode.or: - result = LLVM.BuildOr(_builder, left, right, "or"); - break; - case ILOpcode.xor: - result = LLVM.BuildXor(_builder, left, right, "xor"); - break; - // TODO: Overflow checks - case ILOpcode.add_ovf: - case ILOpcode.add_ovf_un: - result = LLVM.BuildAdd(_builder, left, right, "add"); - break; - case ILOpcode.sub_ovf: - case ILOpcode.sub_ovf_un: - result = LLVM.BuildSub(_builder, left, right, "sub"); - break; - case ILOpcode.mul_ovf: - case ILOpcode.mul_ovf_un: - result = LLVM.BuildMul(_builder, left, right, "mul"); - break; - - default: - throw new InvalidOperationException(); // Should be unreachable + if (kind == StackValueKind.NativeInt || kind == StackValueKind.ByRef || kind == StackValueKind.ObjRef) + { + //we need to put the type back if we changed it because it started out a pointer + result = CastToTypeDesc(result, type); } - - PushExpression(kind, "", result, type); + PushExpression(kind, "binop", result, type); } private void ImportShiftOperation(ILOpcode opcode) { LLVMValueRef result; - StackEntry numBitsToShift = _stack.Pop(); StackEntry valueToShift = _stack.Pop(); + LLVMValueRef valueToShiftValue = valueToShift.ValueForStackKind(valueToShift.Kind, _builder, false); + switch (opcode) { case ILOpcode.shl: - result = LLVM.BuildShl(_builder, valueToShift.LLVMValue, numBitsToShift.LLVMValue, "shl"); + result = LLVM.BuildShl(_builder, valueToShiftValue, numBitsToShift.ValueAsInt32(_builder, false), "shl"); break; case ILOpcode.shr: - result = LLVM.BuildAShr(_builder, valueToShift.LLVMValue, numBitsToShift.LLVMValue, "shr"); + result = LLVM.BuildAShr(_builder, valueToShiftValue, numBitsToShift.ValueAsInt32(_builder, false), "shr"); break; case ILOpcode.shr_un: - result = LLVM.BuildLShr(_builder, valueToShift.LLVMValue, numBitsToShift.LLVMValue, "shr"); + result = LLVM.BuildLShr(_builder, valueToShiftValue, numBitsToShift.ValueAsInt32(_builder, false), "shr"); break; default: throw new InvalidOperationException(); // Should be unreachable } - PushExpression(valueToShift.Kind, "", result, valueToShift.Type); + PushExpression(valueToShift.Kind, "shiftop", result, valueToShift.Type); } private void ImportCompareOperation(ILOpcode opcode) @@ -1185,34 +1345,8 @@ namespace Internal.IL } LLVMValueRef result; - //TODO: deal with sign extension here instead of just casting - var typeSaneOp1 = op1.LLVMValue; - var typeSaneOp2 = op2.LLVMValue; - if (op1.Type != op2.Type || op1.Type == null) - { - if (op1.Type != null && op2.Type != null) - { - if (op1.Type.IsPrimitive && op2.Type.IsPrimitive) - { - if (op1.Type.GetElementSize().AsInt > op2.Type.GetElementSize().AsInt) - typeSaneOp2 = CastIfNecessary(op2.LLVMValue, GetLLVMTypeForTypeDesc(op1.Type)); - else - typeSaneOp1 = CastIfNecessary(op1.LLVMValue, GetLLVMTypeForTypeDesc(op2.Type)); - } - else - { - typeSaneOp2 = CastIfNecessary(op2.LLVMValue, GetLLVMTypeForTypeDesc(op1.Type)); - } - } - else if (op1.Type == null && op1.Kind == StackValueKind.ObjRef) - { - typeSaneOp1 = CastIfNecessary(op1.LLVMValue, LLVM.TypeOf(typeSaneOp2)); - } - else if (op2.Type == null && op2.Kind == StackValueKind.ObjRef) - { - typeSaneOp2 = CastIfNecessary(op2.LLVMValue, LLVM.TypeOf(typeSaneOp1)); - } - } + LLVMValueRef typeSaneOp1 = op1.ValueForStackKind(kind, _builder, true); + LLVMValueRef typeSaneOp2 = op2.ValueForStackKind(kind, _builder, true); switch (opcode) { @@ -1235,23 +1369,30 @@ namespace Internal.IL throw new NotSupportedException(); // unreachable } - PushExpression(kind, "", result, GetWellKnownType(WellKnownType.SByte)); + PushExpression(StackValueKind.Int32, "cmpop", result, GetWellKnownType(WellKnownType.SByte)); } private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) { StackEntry value = _stack.Pop(); - StackEntry convertedValue = value.Duplicate(); + LLVMValueRef convertedValue; //conv.u for a pointer should change to a int8* - if(wellKnownType == WellKnownType.UIntPtr) + if (wellKnownType == WellKnownType.UIntPtr) { if (value.Kind == StackValueKind.Int32) { - convertedValue.LLVMValue = LLVM.BuildIntToPtr(_builder, value.LLVMValue, LLVM.PointerType(LLVM.Int8Type(), 0), "conv.u"); + convertedValue = LLVM.BuildIntToPtr(_builder, value.ValueAsInt32(_builder, false), LLVM.PointerType(LLVM.Int8Type(), 0), "conv.u"); + } + else + { + convertedValue = value.ValueAsType(GetWellKnownType(wellKnownType), _builder); } } - - _stack.Push(convertedValue); + else + { + convertedValue = value.ValueAsType(GetWellKnownType(wellKnownType), _builder); + } + PushExpression(value.Kind, "conv", convertedValue, value.Type); } private void ImportUnaryOperation(ILOpcode opCode) @@ -1264,21 +1405,21 @@ namespace Internal.IL case ILOpcode.neg: if (argument.Kind == StackValueKind.Float) { - result = LLVM.BuildFNeg(_builder, argument.LLVMValue, "neg"); + result = LLVM.BuildFNeg(_builder, argument.ValueForStackKind(argument.Kind, _builder, false), "neg"); } else { - result = LLVM.BuildNeg(_builder, argument.LLVMValue, "neg"); + result = LLVM.BuildNeg(_builder, argument.ValueForStackKind(argument.Kind, _builder, true), "neg"); } break; case ILOpcode.not: - result = LLVM.BuildNot(_builder, argument.LLVMValue, "not"); + result = LLVM.BuildNot(_builder, argument.ValueForStackKind(argument.Kind, _builder, true), "not"); break; default: throw new NotSupportedException(); // unreachable } - PushExpression(argument.Kind, "", result, argument.Type); + PushExpression(argument.Kind, "unaryop", result, argument.Type); } private void ImportCpOpj(int token) @@ -1310,21 +1451,16 @@ namespace Internal.IL if (ldtokenValue is TypeDesc) { ldtokenKind = WellKnownType.RuntimeTypeHandle; - //AddTypeReference((TypeDesc)ldtokenValue, false); - - // todo: this doesn't work because we don't have the eetypeptr pushed. How do we get the eetypeptr? + PushExpression(StackValueKind.ByRef, "ldtoken", GetEETypeForTypeDesc(ldtokenValue as TypeDesc), _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr")); MethodDesc helper = _compilation.TypeSystemContext.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeTypeHandle"); - //AddMethodReference(helper); + AddMethodReference(helper); HandleCall(helper); name = ldtokenValue.ToString(); - - //value = new LdTokenEntry<TypeDesc>(StackValueKind.ValueType, name, (TypeDesc)ldtokenValue, GetWellKnownType(ldtokenKind)); } else if (ldtokenValue is FieldDesc) { ldtokenKind = WellKnownType.RuntimeFieldHandle; - // todo: this is probably the wrong llvm value for the field - value = new LdTokenEntry<FieldDesc>(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, LLVM.ConstInt(LLVM.Int32Type(), (uint)token, LLVMMisc.False), GetWellKnownType(ldtokenKind)); + value = new LdTokenEntry<FieldDesc>(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, GetWellKnownType(ldtokenKind)); _stack.Push(value); } else if (ldtokenValue is MethodDesc) @@ -1404,21 +1540,37 @@ namespace Internal.IL private LLVMValueRef GetInstanceFieldAddress(StackEntry objectEntry, FieldDesc field) { var objectType = objectEntry.Type ?? field.OwningType; - LLVMValueRef typedObjectValue; + LLVMValueRef untypedObjectValue; + LLVMTypeRef llvmObjectType = GetLLVMTypeForTypeDesc(objectType); + if (objectType.IsValueType && !objectType.IsPointer && objectEntry.Kind != StackValueKind.NativeInt && objectEntry.Kind != StackValueKind.ByRef) { - typedObjectValue = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(objectType), "objptr"); - LLVM.BuildStore(_builder, objectEntry.LLVMValue, typedObjectValue); + if (objectEntry is LoadExpressionEntry) + { + untypedObjectValue = CastToRawPointer(((LoadExpressionEntry)objectEntry).RawLLVMValue); + } + else + { + untypedObjectValue = LLVM.BuildAlloca(_builder, llvmObjectType, "objptr"); + LLVM.BuildStore(_builder, objectEntry.ValueAsType(llvmObjectType, _builder), untypedObjectValue); + untypedObjectValue = LLVM.BuildPointerCast(_builder, untypedObjectValue, LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), "objptrcast"); + } } else { - typedObjectValue = objectEntry.LLVMValue; + untypedObjectValue = objectEntry.ValueAsType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), _builder); } - var untypedObjectPointer = CastIfNecessary(typedObjectValue, LLVM.PointerType(LLVMTypeRef.Int8Type(), 0)); - var loadLocation = LLVM.BuildGEP(_builder, untypedObjectPointer, - new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)field.Offset.AsInt, LLVMMisc.False) }, String.Empty); - return LLVM.BuildPointerCast(_builder, loadLocation, LLVM.PointerType(GetLLVMTypeForTypeDesc(field.FieldType), 0), "fieldaddresscast"); + if (field.Offset.AsInt == 0) + { + return untypedObjectValue; + } + else + { + var loadLocation = LLVM.BuildGEP(_builder, untypedObjectValue, + new LLVMValueRef[] { LLVM.ConstInt(LLVM.Int32Type(), (ulong)field.Offset.AsInt, LLVMMisc.False) }, String.Empty); + return loadLocation; + } } private LLVMValueRef GetFieldAddress(FieldDesc field, bool isStatic) @@ -1429,8 +1581,7 @@ namespace Internal.IL if (!isStatic) _stack.Pop(); - LLVMValueRef untypedFieldAddress = WebAssemblyObjectWriter.EmitGlobal(Module, field, _compilation.NameMangler); - return LLVM.BuildPointerCast(_builder, untypedFieldAddress, LLVM.PointerType(GetLLVMTypeForTypeDesc(field.FieldType), 0), "tempfieldaddresscast"); + return WebAssemblyObjectWriter.EmitGlobal(Module, field, _compilation.NameMangler); } else { @@ -1442,26 +1593,22 @@ namespace Internal.IL { FieldDesc field = (FieldDesc)_methodIL.GetObject(token); LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic); - LLVMValueRef loadValue = LLVM.BuildLoad(_builder, fieldAddress, "ldfld_" + field.Name); - PushExpression(GetStackValueKind(field.FieldType), "ldfld", loadValue, field.FieldType); + PushLoadExpression(GetStackValueKind(field.FieldType), "ldfld_" + field.Name, fieldAddress, field.FieldType); } private void ImportAddressOfField(int token, bool isStatic) { FieldDesc field = (FieldDesc)_methodIL.GetObject(token); LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic); - PushExpression(StackValueKind.ByRef, "ldflda", fieldAddress, field.FieldType.MakePointerType()); + _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldflda", fieldAddress, field.FieldType.MakePointerType())); } private void ImportStoreField(int token, bool isStatic) { FieldDesc field = (FieldDesc)_methodIL.GetObject(token); StackEntry valueEntry = _stack.Pop(); - LLVMValueRef value = valueEntry.LLVMValue; - - value = CastIfNecessary(value, GetLLVMTypeForTypeDesc(field.FieldType)); LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic); - LLVM.BuildStore(_builder, value, fieldAddress); + CastingStore(fieldAddress, valueEntry, field.FieldType); } // Loads symbol address. Address is represented as a i32* @@ -1485,10 +1632,24 @@ namespace Internal.IL private void ImportInitObj(int token) { + TypeDesc type = ResolveTypeToken(token); + var valueEntry = _stack.Pop(); + var llvmType = GetLLVMTypeForTypeDesc(type); + if (llvmType.TypeKind == LLVMTypeKind.LLVMArrayTypeKind) + ImportCallMemset(valueEntry.ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder), 0, type.GetElementSize().AsInt); + else if (llvmType.TypeKind == LLVMTypeKind.LLVMIntegerTypeKind) + LLVM.BuildStore(_builder, LLVM.ConstInt(llvmType, 0, LLVMMisc.False), valueEntry.ValueAsType(LLVM.PointerType(llvmType, 0), _builder)); + else if (llvmType.TypeKind == LLVMTypeKind.LLVMPointerTypeKind) + LLVM.BuildStore(_builder, LLVM.ConstNull(llvmType), valueEntry.ValueAsType(LLVM.PointerType(llvmType, 0), _builder)); + else if (llvmType.TypeKind == LLVMTypeKind.LLVMFloatTypeKind) + LLVM.BuildStore(_builder, LLVM.ConstReal(llvmType, 0.0), valueEntry.ValueAsType(LLVM.PointerType(llvmType, 0), _builder)); + else + throw new NotImplementedException(); } private void ImportBox(int token) { + } private void ImportLeave(BasicBlock target) @@ -1573,38 +1734,48 @@ namespace Internal.IL if (_stack.Length > 0) { entryStack = new EvaluationStack<StackEntry>(_stack.Length); - -#pragma warning disable 162 // Due to not implement3ed exception incrementer in for needs pragma warning disable for (int i = 0; i < _stack.Length; i++) { - // todo: do we need anything special for spilled stacks like cpp codegen does? - entryStack.Push(_stack[i]); - //entryStack.Push(NewSpillSlot(_stack[i])); + entryStack.Push(NewSpillSlot(_stack[i])); } -#pragma warning restore 162 } next.EntryStack = entryStack; } if (entryStack != null) { - // todo: do we have to do anything here? -#pragma warning disable 162// Due to not implement3ed exception incrementer in for needs pragma warning disable for (int i = 0; i < entryStack.Length; i++) { - /*AppendLine(); - Append(entryStack[i]); - Append(" = "); - Append(_stack[i]); - AppendSemicolon();*/ + var currentEntry = _stack[i]; + var entry = entryStack[i] as SpilledExpressionEntry; + if (entry == null) + throw new InvalidProgramException(); + + if (currentEntry is SpilledExpressionEntry) + continue; //this is already a sharable value + + StoreTemp(entry.LocalIndex, currentEntry.ValueAsType(entry.Type, _builder)); } -#pragma warning restore 162 } MarkBasicBlock(next); } + private StackEntry NewSpillSlot(StackEntry entry) + { + if (entry is SpilledExpressionEntry) + return entry; + else + { + var entryType = entry.Type ?? GetWellKnownType(WellKnownType.Object); //type is required here, currently the only time entry.Type is null is if someone has pushed a null literal + var entryIndex = _spilledExpressions.Count; + var newEntry = new SpilledExpressionEntry(entry.Kind, entry is ExpressionEntry ? ((ExpressionEntry)entry).Name : "spilled" + entryIndex, entryType, entryIndex, this); + _spilledExpressions.Add(newEntry); + return newEntry; + } + } + private TypeDesc ResolveTypeToken(int token) { return (TypeDesc)_methodIL.GetObject(token); diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs index 8a814508e..3a5f48117 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs @@ -101,7 +101,6 @@ namespace Internal.IL static LLVMValueRef TrapFunction = default(LLVMValueRef); static LLVMValueRef DoNothingFunction = default(LLVMValueRef); - private static IEnumerable<string> GetParameterNamesForMethod(MethodDesc method) { // TODO: The uses of this method need revision. The right way to get to this info is from diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs index 335157917..140e2b1bc 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs @@ -188,6 +188,17 @@ namespace ILCompiler.DependencyAnalysis //throw new NotImplementedException(); // This function isn't complete } + public static LLVMValueRef GetConstZeroArray(int length) + { + var int8Type = LLVM.Int8Type(); + var result = new LLVMValueRef[length]; + for (int i = 0; i < length; i++) + { + result[i] = LLVM.ConstInt(int8Type, 0, LLVMMisc.False); + } + return LLVM.ConstArray(int8Type, result); + } + public static LLVMValueRef EmitGlobal(LLVMModuleRef module, FieldDesc field, NameMangler nameMangler) { if (field.IsThreadStatic) @@ -203,7 +214,7 @@ namespace ILCompiler.DependencyAnalysis var valueType = LLVM.ArrayType(LLVM.Int8Type(), (uint)field.FieldType.GetElementSize().AsInt); var llvmValue = LLVM.AddGlobal(module, valueType, nameMangler.GetMangledFieldName(field).ToString()); LLVM.SetLinkage(llvmValue, LLVMLinkage.LLVMInternalLinkage); - LLVM.SetInitializer(llvmValue, LLVM.ConstPointerNull(valueType)); + LLVM.SetInitializer(llvmValue, GetConstZeroArray(field.FieldType.GetElementSize().AsInt)); s_staticFieldMapping.Add(field, llvmValue); return llvmValue; } @@ -262,7 +273,7 @@ namespace ILCompiler.DependencyAnalysis { LLVMValueRef valRef = IsFunction ? LLVM.GetNamedFunction(module, SymbolName) : LLVM.GetNamedGlobal(module, SymbolName); - if (Offset != 0) + if (Offset != 0 && valRef.Pointer != IntPtr.Zero) { var pointerType = LLVM.PointerType(LLVM.Int8Type(), 0); var bitCast = LLVM.ConstBitCast(valRef, pointerType); @@ -313,8 +324,16 @@ namespace ILCompiler.DependencyAnalysis if (ObjectSymbolRefs.TryGetValue(curOffset, out symbolRef)) { LLVMValueRef pointedAtValue = symbolRef.ToLLVMValueRef(module); - var ptrValue = LLVM.ConstBitCast(pointedAtValue, intPtrType); - entries.Add(ptrValue); + //TODO: why did this come back null + if (pointedAtValue.Pointer != IntPtr.Zero) + { + var ptrValue = LLVM.ConstBitCast(pointedAtValue, intPtrType); + entries.Add(ptrValue); + } + else + { + entries.Add(LLVM.ConstPointerNull(intPtrType)); + } } else { @@ -358,7 +377,6 @@ namespace ILCompiler.DependencyAnalysis _dataToFill.Add(new ObjectNodeDataEmission(arrayglobal, _currentObjectData.ToArray(), _currentObjectSymbolRefs)); - foreach (var symbolIdInfo in _symbolDefs) { EmitSymbolDef(arrayglobal, symbolIdInfo.Key, symbolIdInfo.Value); diff --git a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs index 039969de2..978e29f8e 100644 --- a/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs +++ b/src/ILCompiler.WebAssembly/src/Compiler/DependencyAnalysis/WebAssemblyCodegenNodeFactory.cs @@ -32,8 +32,8 @@ namespace ILCompiler.DependencyAnalysis protected override IMethodNode CreateUnboxingStubNode(MethodDesc method) { - // TODO: this is wrong: this returns an assembly stub node - return new UnboxingStubNode(method, Target); + // TODO: this is wrong: this returns an unstubbed node + return new WebAssemblyMethodCodeNode(method); } protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall) |