From 3b9a7fc3fc60fdca245f431bfa40672f087a53d5 Mon Sep 17 00:00:00 2001 From: Jeff Greene Date: Tue, 31 Oct 2017 17:41:14 -0700 Subject: implemented newobj for WASM using malloc (#4808) implemented newobj using malloc, added evaluation stack spillage and cut down on unneeded llvm casts --- .../src/CodeGen/EvaluationStack.cs | 267 +++++--- .../src/CodeGen/ILToWebAssemblyImporter.cs | 762 +++++++++++++-------- .../src/CodeGen/ILToWebAssemblyImporter_Statics.cs | 1 - .../src/CodeGen/WebAssemblyObjectWriter.cs | 28 +- .../WebAssemblyCodegenNodeFactory.cs | 4 +- tests/src/Simple/HelloWasm/Program.cs | 25 +- 6 files changed, 699 insertions(+), 388 deletions(-) diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs index 98f862c54..de79dd67f 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 /// Position where to insert 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) { @@ -166,6 +167,20 @@ namespace Internal.IL } } + class LLVMTypeRefEqualityComparer : IEqualityComparer + { + public static LLVMTypeRefEqualityComparer Instance = new LLVMTypeRefEqualityComparer(); + public bool Equals(LLVMTypeRef x, LLVMTypeRef y) + { + return x.Pointer.Equals(y.Pointer); + } + + public int GetHashCode(LLVMTypeRef obj) + { + return obj.Pointer.GetHashCode(); + } + } + /// /// Abstract representation of a stack entry /// @@ -181,18 +196,53 @@ namespace Internal.IL /// public TypeDesc Type { get; } - public LLVMValueRef LLVMValue { get; set; } + Dictionary _castValues = new Dictionary(LLVMTypeRefEqualityComparer.Instance); + + 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); /// /// Initializes a new instance of StackEntry. /// /// Kind of entry. /// Type if any of entry. - protected StackEntry(StackValueKind kind, LLVMValueRef llvmValue, TypeDesc type = null) + protected StackEntry(StackValueKind kind, TypeDesc type = null) { Kind = kind; Type = type; - LLVMValue = llvmValue; } /// @@ -206,45 +256,6 @@ namespace Internal.IL /// /// A new instance of the same type as the current entry. public abstract StackEntry Duplicate(); - - /// - /// Overridden and sealed to force descendants to override . - /// - /// String representation of current entry - public override sealed string ToString() - { - StringBuilder s = new StringBuilder(); - BuildRepresentation(s); - return s.ToString(); - } - - /// - /// Build a representation of current entry in . - /// - /// StringBuilder where representation will be saved. - 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(')'); - } - } - } } /// @@ -252,7 +263,7 @@ namespace Internal.IL /// 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 +282,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 { - 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 +345,7 @@ namespace Internal.IL internal class Int64ConstantEntry : ConstantEntry { - 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 +354,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 +404,15 @@ namespace Internal.IL internal class FloatConstantEntry : ConstantEntry { - 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 +428,77 @@ namespace Internal.IL /// String representation of current expression /// public string Name { get; set; } - + public LLVMValueRef RawLLVMValue { get; set; } /// /// Initializes new instance of ExpressionEntry /// /// Kind of entry /// String representation of entry /// Type if any of entry - 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 + { + /// + /// Initializes new instance of ExpressionEntry + /// + /// Kind of entry + /// String representation of entry + /// Type if any of entry + 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 + { + /// + /// Initializes new instance of ExpressionEntry + /// + /// Kind of entry + /// String representation of entry + /// Type if any of entry + 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); + } + } + /// /// Entry representing some token (either of TypeDesc, MethodDesc or FieldDesc) along with its string representation /// @@ -417,21 +506,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(Kind, Name, LdToken, LLVMValue, Type); + return new LdTokenEntry(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 +529,7 @@ namespace Internal.IL /// public static InvalidEntry Entry = new InvalidEntry(); - protected InvalidEntry() : base(StackValueKind.Unknown, default(LLVMValueRef), null) + protected InvalidEntry() : base(StackValueKind.Unknown, null) { } @@ -451,9 +538,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(); + } + } + + /// + /// Entry representing a writable sharable stack entry that can survive from one basic block to another + /// + 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..c41d66da7 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 _dependencies = new ArrayBuilder(); public IEnumerable GetDependencies() { @@ -35,6 +42,7 @@ namespace Internal.IL private LLVMBasicBlockRef _curBasicBlock; private LLVMBuilderRef _builder; private readonly LocalVariableDefinition[] _locals; + private List _spilledExpressions = new List(); 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)); + } + /// /// Push an expression named of kind . /// @@ -194,45 +226,6 @@ namespace Internal.IL _stack.Push(new ExpressionEntry(kind, name, llvmValue, type)); } - - - /// - /// Generate a cast in case the stack type of source is not identical or compatible with destination type. - /// - /// Type of destination - /// Source entry from stack - 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])); } } @@ -303,14 +298,73 @@ 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 (signExtend && type.GetIntTypeWidth() > LLVM.TypeOf(value).GetIntTypeWidth()) + { + return LLVM.BuildSExtOrBitCast(builder, value, type, "SExtOrBitCast"); + } + else if (LLVM.TypeOf(value).TypeKind == LLVMTypeKind.LLVMPointerTypeKind) + { + return LLVM.BuildPtrToInt(builder, value, type, "intcast"); + } + 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,7 +470,33 @@ 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) @@ -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: @@ -520,6 +612,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"); + } + /// /// Implements intrinsic methods instread of calling them /// @@ -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(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(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, LLVM.ConstInt(LLVM.Int32Type(), (uint)token, LLVMMisc.False), GetWellKnownType(ldtokenKind)); + value = new LdTokenEntry(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, GetWellKnownType(ldtokenKind)); _stack.Push(value); } else if (ldtokenValue is MethodDesc) @@ -1404,21 +1540,26 @@ namespace Internal.IL private LLVMValueRef GetInstanceFieldAddress(StackEntry objectEntry, FieldDesc field) { var objectType = objectEntry.Type ?? field.OwningType; - LLVMValueRef typedObjectValue; - if (objectType.IsValueType && !objectType.IsPointer && objectEntry.Kind != StackValueKind.NativeInt && objectEntry.Kind != StackValueKind.ByRef) + LLVMValueRef untypedObjectValue; + LLVMTypeRef llvmObjectType = GetLLVMTypeForTypeDesc(objectType); + if (objectEntry is LoadExpressionEntry) { - typedObjectValue = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(objectType), "objptr"); - LLVM.BuildStore(_builder, objectEntry.LLVMValue, typedObjectValue); + untypedObjectValue = CastToRawPointer(((LoadExpressionEntry)objectEntry).RawLLVMValue); + } + else if (objectType.IsValueType && !objectType.IsPointer && objectEntry.Kind != StackValueKind.NativeInt && objectEntry.Kind != StackValueKind.ByRef) + { + 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, + var loadLocation = LLVM.BuildGEP(_builder, untypedObjectValue, 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"); + return loadLocation; } private LLVMValueRef GetFieldAddress(FieldDesc field, bool isStatic) @@ -1429,8 +1570,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 +1582,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 +1621,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 +1723,48 @@ namespace Internal.IL if (_stack.Length > 0) { entryStack = new EvaluationStack(_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 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) diff --git a/tests/src/Simple/HelloWasm/Program.cs b/tests/src/Simple/HelloWasm/Program.cs index 453a8555e..2cb0eb895 100644 --- a/tests/src/Simple/HelloWasm/Program.cs +++ b/tests/src/Simple/HelloWasm/Program.cs @@ -11,10 +11,12 @@ internal static class Program private static unsafe void Main(string[] args) { Add(1, 2); - + var tempObj = new TestClass(1337); int tempInt = 0; (*(&tempInt)) = 9; + tempObj.TestMethod("Hello"); + if(tempInt == 9) { PrintLine("Hello from C#!"); @@ -22,6 +24,8 @@ internal static class Program TwoByteStr str = new TwoByteStr() { first = 1, second = 2 }; TwoByteStr str2 = new TwoByteStr() { first = 3, second = 4 }; + *(&str) = str2; + str2 = *(&str); if (str2.second == 4) { @@ -57,6 +61,7 @@ internal static class Program { PrintLine("shiftRight test: Ok."); } + var unsignedShift = UnsignedShift(0xFFFFFFFFu, 4) == 0x0FFFFFFFu; if (unsignedShift) { @@ -159,3 +164,21 @@ public struct TwoByteStr public byte second; } +public class TestClass +{ + public string TestString {get; set;} + + public TestClass(int number) + { + if(number != 1337) + throw new Exception(); + } + + public void TestMethod(string str) + { + TestString = str; + if(TestString != str) + throw new Exception(); + } +} + -- cgit v1.2.3 From 4d824e261a7223bf2a36d5b6c85d5c8ed93a205a Mon Sep 17 00:00:00 2001 From: Samuel Arzt Date: Thu, 2 Nov 2017 21:05:34 +0100 Subject: Added net46 as second target framework. (#4862) --- src/ILVerify/src/AccessVerificationHelpers.cs | 2 +- src/ILVerify/src/ILVerify.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ILVerify/src/AccessVerificationHelpers.cs b/src/ILVerify/src/AccessVerificationHelpers.cs index 37064f850..9274cad54 100644 --- a/src/ILVerify/src/AccessVerificationHelpers.cs +++ b/src/ILVerify/src/AccessVerificationHelpers.cs @@ -262,7 +262,7 @@ namespace ILVerify foreach (var attribute in assembly.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "InternalsVisibleToAttribute")) { - var friendValues = ((string)attribute.FixedArguments[0].Value).Split(", "); + var friendValues = ((string)attribute.FixedArguments[0].Value).Split(new string[] { ", " }, StringSplitOptions.None); if (friendValues.Length >= 1 && friendValues.Length <= 2) { if (friendValues[0] != friendName.Name) diff --git a/src/ILVerify/src/ILVerify.csproj b/src/ILVerify/src/ILVerify.csproj index 73c0208a6..d07d962fa 100644 --- a/src/ILVerify/src/ILVerify.csproj +++ b/src/ILVerify/src/ILVerify.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + netcoreapp2.0;net46 false true false @@ -294,7 +294,7 @@ - + -- cgit v1.2.3 From 9eb948aa36ed84898502f01ffc3d4533de875f43 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 2 Nov 2017 13:06:38 -0700 Subject: Port https://github.com/dotnet/coreclr/pull/14822 (#4861) --- src/Native/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Native/CMakeLists.txt b/src/Native/CMakeLists.txt index 5aeb930a5..e77b1b6f4 100644 --- a/src/Native/CMakeLists.txt +++ b/src/Native/CMakeLists.txt @@ -224,6 +224,7 @@ if(WIN32) add_compile_options($<$:-DDEBUG>) add_compile_options($<$:/MTd>) add_compile_options($<$:/MT>) + add_compile_options(/source-charset:utf-8) # Force MSVC to compile source as UTF-8. add_compile_options(/Zi) # enable debugging information set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG") -- cgit v1.2.3 From f760ef3415757e50e30f9af769965bd2a9f6a4de Mon Sep 17 00:00:00 2001 From: Morgan Brown Date: Fri, 3 Nov 2017 02:18:19 -0700 Subject: Revert "implemented newobj for WASM using malloc (#4808)" (#4864) This reverts commit 3b9a7fc3fc60fdca245f431bfa40672f087a53d5. --- .../src/CodeGen/EvaluationStack.cs | 267 +++----- .../src/CodeGen/ILToWebAssemblyImporter.cs | 762 ++++++++------------- .../src/CodeGen/ILToWebAssemblyImporter_Statics.cs | 1 + .../src/CodeGen/WebAssemblyObjectWriter.cs | 28 +- .../WebAssemblyCodegenNodeFactory.cs | 4 +- tests/src/Simple/HelloWasm/Program.cs | 25 +- 6 files changed, 388 insertions(+), 699 deletions(-) diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs index de79dd67f..98f862c54 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/EvaluationStack.cs @@ -9,7 +9,6 @@ using ILCompiler.Compiler.CppCodeGen; using Internal.TypeSystem; using LLVMSharp; using ILCompiler.CodeGen; -using System.Collections.Generic; namespace Internal.IL { @@ -87,7 +86,7 @@ namespace Internal.IL /// Position where to insert 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) { @@ -167,20 +166,6 @@ namespace Internal.IL } } - class LLVMTypeRefEqualityComparer : IEqualityComparer - { - public static LLVMTypeRefEqualityComparer Instance = new LLVMTypeRefEqualityComparer(); - public bool Equals(LLVMTypeRef x, LLVMTypeRef y) - { - return x.Pointer.Equals(y.Pointer); - } - - public int GetHashCode(LLVMTypeRef obj) - { - return obj.Pointer.GetHashCode(); - } - } - /// /// Abstract representation of a stack entry /// @@ -196,53 +181,18 @@ namespace Internal.IL /// public TypeDesc Type { get; } - Dictionary _castValues = new Dictionary(LLVMTypeRefEqualityComparer.Instance); - - 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); + public LLVMValueRef LLVMValue { get; set; } /// /// Initializes a new instance of StackEntry. /// /// Kind of entry. /// Type if any of entry. - protected StackEntry(StackValueKind kind, TypeDesc type = null) + protected StackEntry(StackValueKind kind, LLVMValueRef llvmValue, TypeDesc type = null) { Kind = kind; Type = type; + LLVMValue = llvmValue; } /// @@ -256,6 +206,45 @@ namespace Internal.IL /// /// A new instance of the same type as the current entry. public abstract StackEntry Duplicate(); + + /// + /// Overridden and sealed to force descendants to override . + /// + /// String representation of current entry + public override sealed string ToString() + { + StringBuilder s = new StringBuilder(); + BuildRepresentation(s); + return s.ToString(); + } + + /// + /// Build a representation of current entry in . + /// + /// StringBuilder where representation will be saved. + 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(')'); + } + } + } } /// @@ -263,7 +252,7 @@ namespace Internal.IL /// internal abstract class ConstantEntry : StackEntry { - protected ConstantEntry(StackValueKind kind, TypeDesc type = null) : base(kind, type) + protected ConstantEntry(StackValueKind kind, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, llvmValue, type) { } @@ -282,36 +271,26 @@ namespace Internal.IL { public T Value { get; } - protected ConstantEntry(StackValueKind kind, T value, TypeDesc type = null) : base(kind, type) + protected ConstantEntry(StackValueKind kind, T value, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, llvmValue, type) { Value = value; } - } - internal class Int32ConstantEntry : ConstantEntry - { - public Int32ConstantEntry(int value, TypeDesc type = null) : base(StackValueKind.Int32, value, type) + protected override void BuildRepresentation(StringBuilder s) { + base.BuildRepresentation(s); + if (s.Length > 0) + { + s.Append(' '); + } + s.Append(Value); } + } - protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + internal class Int32ConstantEntry : ConstantEntry + { + public Int32ConstantEntry(int value, TypeDesc type = null) : base(StackValueKind.Int32, value, LLVM.ConstInt(LLVM.Int32Type(), (ulong)value, LLVMMisc.False), type) { - 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() @@ -345,7 +324,7 @@ namespace Internal.IL internal class Int64ConstantEntry : ConstantEntry { - public Int64ConstantEntry(long value, TypeDesc type = null) : base(StackValueKind.Int64, value, type) + public Int64ConstantEntry(long value, TypeDesc type = null) : base(StackValueKind.Int64, value, LLVM.ConstInt(LLVM.Int64Type(), (ulong)value, LLVMMisc.False), type) { } @@ -354,26 +333,6 @@ 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) @@ -404,15 +363,10 @@ namespace Internal.IL internal class FloatConstantEntry : ConstantEntry { - public FloatConstantEntry(double value, TypeDesc type = null) : base(StackValueKind.Float, value, type) + public FloatConstantEntry(double value, TypeDesc type = null) : base(StackValueKind.Float, value, LLVM.ConstReal(LLVM.FloatType(), 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); @@ -428,77 +382,34 @@ namespace Internal.IL /// String representation of current expression /// public string Name { get; set; } - public LLVMValueRef RawLLVMValue { get; set; } - /// - /// Initializes new instance of ExpressionEntry - /// - /// Kind of entry - /// String representation of entry - /// Type if any of entry - 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, RawLLVMValue, Type); - } - - protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) - { - //TODO: deal with sign extension here - return ILImporter.CastIfNecessary(builder, RawLLVMValue, type); - } - } - - internal class LoadExpressionEntry : ExpressionEntry - { /// /// Initializes new instance of ExpressionEntry /// /// Kind of entry /// String representation of entry /// Type if any of entry - 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 - { - /// - /// Initializes new instance of ExpressionEntry - /// - /// Kind of entry - /// String representation of entry - /// Type if any of entry - public AddressExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, name, llvmValue, type) + public ExpressionEntry(StackValueKind kind, string name, LLVMValueRef llvmValue, TypeDesc type = null) : base(kind, llvmValue, type) { + Name = name; } public override StackEntry Duplicate() { - return new LoadExpressionEntry(Kind, Name, RawLLVMValue, Type); + return new ExpressionEntry(Kind, Name, LLVMValue, Type); } - protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + protected override void BuildRepresentation(StringBuilder s) { - return ILImporter.CastIfNecessary(builder, RawLLVMValue, type); + base.BuildRepresentation(s); + if (s.Length > 0) + { + s.Append(' '); + } + s.Append(Name); } } - + /// /// Entry representing some token (either of TypeDesc, MethodDesc or FieldDesc) along with its string representation /// @@ -506,19 +417,21 @@ namespace Internal.IL { public T LdToken { get; } - public LdTokenEntry(StackValueKind kind, string name, T token, TypeDesc type = null) : base(kind, name, default(LLVMValueRef), type) + public LdTokenEntry(StackValueKind kind, string name, T token, LLVMValueRef value, TypeDesc type = null) : base(kind, name, value, type) { LdToken = token; } public override StackEntry Duplicate() { - return new LdTokenEntry(Kind, Name, LdToken, Type); + return new LdTokenEntry(Kind, Name, LdToken, LLVMValue, Type); } - protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) + protected override void BuildRepresentation(StringBuilder s) { - return ILImporter.CastIfNecessary(builder, RawLLVMValue, type); + base.BuildRepresentation(s); + s.Append(' '); + s.Append(LdToken); } } @@ -529,7 +442,7 @@ namespace Internal.IL /// public static InvalidEntry Entry = new InvalidEntry(); - protected InvalidEntry() : base(StackValueKind.Unknown, null) + protected InvalidEntry() : base(StackValueKind.Unknown, default(LLVMValueRef), null) { } @@ -538,33 +451,9 @@ namespace Internal.IL return this; } - protected override LLVMValueRef ValueAsTypeInternal(LLVMTypeRef type, LLVMBuilderRef builder, bool signExtend) - { - throw new InvalidOperationException(); - } - } - - /// - /// Entry representing a writable sharable stack entry that can survive from one basic block to another - /// - 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() + protected override void BuildRepresentation(StringBuilder s) { - return new SpilledExpressionEntry(Kind, Name, Type, LocalIndex, _importer); + s.Append("Invalid Entry"); } } } diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs index c41d66da7..8c7886288 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs @@ -19,13 +19,6 @@ 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 _dependencies = new ArrayBuilder(); public IEnumerable GetDependencies() { @@ -42,7 +35,6 @@ namespace Internal.IL private LLVMBasicBlockRef _curBasicBlock; private LLVMBuilderRef _builder; private readonly LocalVariableDefinition[] _locals; - private List _spilledExpressions = new List(); private readonly byte[] _ilBytes; @@ -162,30 +154,6 @@ 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)); - } - /// /// Push an expression named of kind . /// @@ -226,6 +194,45 @@ namespace Internal.IL _stack.Push(new ExpressionEntry(kind, name, llvmValue, type)); } + + + /// + /// Generate a cast in case the stack type of source is not identical or compatible with destination type. + /// + /// Type of destination + /// Source entry from stack + 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() { @@ -274,8 +281,6 @@ 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])); } } @@ -298,73 +303,14 @@ 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 (signExtend && type.GetIntTypeWidth() > LLVM.TypeOf(value).GetIntTypeWidth()) - { - return LLVM.BuildSExtOrBitCast(builder, value, type, "SExtOrBitCast"); - } - else if (LLVM.TypeOf(value).TypeKind == LLVMTypeKind.LLVMPointerTypeKind) - { - return LLVM.BuildPtrToInt(builder, value, type, "intcast"); - } - 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 (kind == LocalVarKind.Argument) + if (argument) { varCountBase = 0; varBase = 0; @@ -389,25 +335,21 @@ namespace Internal.IL } valueType = GetLLVMTypeForTypeDesc(type); } - else if (kind == LocalVarKind.Local) + else { 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; - } - return LLVM.BuildGEP(_builder, LLVM.GetFirstParam(_llvmFunction), + var loadLocation = 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) @@ -453,16 +395,24 @@ namespace Internal.IL private void ImportStoreVar(int index, bool argument) { - TypeDesc varType; - StackEntry toStore = _stack.Pop(); - LLVMValueRef varAddress = LoadVarAddress(index, argument ? LocalVarKind.Argument : LocalVarKind.Local, out varType); - CastingStore(varAddress, toStore, varType); + 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)); } 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); @@ -470,33 +420,7 @@ 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) @@ -508,19 +432,11 @@ 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"); - } - 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"); + typedToStore = LLVM.BuildPtrToInt(_builder, source, valueType, "CastIfNecessaryInt"); } else if (toStoreKind == LLVMTypeKind.LLVMPointerTypeKind && valueTypeKind != LLVMTypeKind.LLVMIntegerTypeKind) { @@ -528,7 +444,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) { @@ -541,21 +457,13 @@ 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"); - } - 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"); + typedToStore = LLVM.BuildIntCast(_builder, source, valueType, "CastIfNecessaryInt"); } return typedToStore; } - internal static LLVMTypeRef GetLLVMTypeForTypeDesc(TypeDesc type) + private LLVMTypeRef GetLLVMTypeForTypeDesc(TypeDesc type) { switch (type.Category) { @@ -573,9 +481,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: @@ -584,7 +492,7 @@ namespace Internal.IL return LLVM.PointerType(LLVM.Int8Type(), 0); case TypeFlags.Pointer: - return LLVM.PointerType(type.GetParameterType().IsVoid ? LLVM.Int8Type() : GetLLVMTypeForTypeDesc(type.GetParameterType()), 0); + return LLVM.PointerType(GetLLVMTypeForTypeDesc(type.GetParameterType()), 0); case TypeFlags.Int64: case TypeFlags.UInt64: @@ -612,16 +520,6 @@ 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++) @@ -694,23 +592,22 @@ namespace Internal.IL } } - private void GetSpillSizeAndOffsetAtIndex(int index, out int size, out int offset) + private void ImportAddressOfVar(int index, bool argument) { - SpilledExpressionEntry spill = _spilledExpressions[index]; - size = spill.Type.GetElementSize().AsInt; - - offset = 0; - for (int i = 0; i < index; i++) + if (argument) { - offset += _spilledExpressions[i].Type.GetElementSize().AsInt; + throw new NotImplementedException("ldarga"); } - } - 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())); + 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 ImportDup() @@ -743,7 +640,8 @@ namespace Internal.IL { StackEntry retVal = _stack.Pop(); LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(_signature.ReturnType); - ImportStoreHelper(retVal.ValueAsType(valueType, _builder), valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0); + + ImportStoreHelper(retVal.LLVMValue, valueType, LLVM.GetNextParam(LLVM.GetFirstParam(_llvmFunction)), 0); } LLVM.BuildRetVoid(_builder); @@ -752,6 +650,7 @@ namespace Internal.IL private void ImportCall(ILOpcode opcode, int token) { MethodDesc callee = (MethodDesc)_methodIL.GetObject(token); + if (callee.IsIntrinsic) { if (ImportIntrinsicCall(callee)) @@ -766,29 +665,8 @@ 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 && opcode != ILOpcode.newobj) + if (opcode != ILOpcode.call && opcode != ILOpcode.callvirt) { throw new NotImplementedException(); } @@ -800,45 +678,6 @@ 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"); - } - /// /// Implements intrinsic methods instread of calling them /// @@ -896,16 +735,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 - StackEntry[] argumentValues = new StackEntry[callee.Signature.Length + instanceAdjustment]; + LLVMValueRef[] argumentValues = new LLVMValueRef[callee.Signature.Length + instanceAdjustment]; for(int i = 0; i < argumentValues.Length; i++) { - argumentValues[argumentValues.Length - i - 1] = _stack.Pop(); + argumentValues[argumentValues.Length - i - 1] = _stack.Pop().LLVMValue; } for (int index = 0; index < argumentValues.Length; index++) { - StackEntry toStore = argumentValues[index]; + LLVMValueRef toStore = argumentValues[index]; TypeDesc argType; if (index == 0 && !callee.Signature.IsStatic) @@ -919,7 +758,7 @@ namespace Internal.IL LLVMTypeRef valueType = GetLLVMTypeForTypeDesc(argType); - ImportStoreHelper(toStore.ValueAsType(valueType, _builder), valueType, castShadowStack, argOffset); + ImportStoreHelper(toStore, valueType, castShadowStack, argOffset); argOffset += (uint)argType.GetElementSize().AsInt; } @@ -933,7 +772,8 @@ namespace Internal.IL { LLVMTypeRef returnLLVMType = GetLLVMTypeForTypeDesc(callee.Signature.ReturnType); LLVMValueRef returnLLVMPointer = LLVM.BuildPointerCast(_builder, returnAddress, LLVM.PointerType(returnLLVMType, 0), "castreturnpointer"); - PushLoadExpression(GetStackValueKind(callee.Signature.ReturnType), String.Empty, returnLLVMPointer, callee.Signature.ReturnType); + LLVMValueRef loadResult = LLVM.BuildLoad(_builder, returnLLVMPointer, String.Empty); + PushExpression(GetStackValueKind(callee.Signature.ReturnType), String.Empty, loadResult, callee.Signature.ReturnType); } } @@ -946,11 +786,6 @@ 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) { @@ -971,17 +806,24 @@ 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]; - arguments[arguments.Length - i - 1] = _stack.Pop().ValueAsType(GetLLVMTypeForTypeDesc(signatureType), _builder); + LLVMValueRef typedValue = argValue; + if (signatureType.IsPointer) + { + LLVMTypeRef signatureLlvmType = GetLLVMTypeForTypeDesc(signatureType); + typedValue = LLVM.BuildPointerCast(_builder, argValue, signatureLlvmType, "castarg"); + } + arguments[arguments.Length - i - 1] = typedValue; } - //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); + var returnValue = LLVM.BuildCall(_builder, nativeFunc, arguments, "call"); if(!method.Signature.ReturnType.IsVoid) - PushExpression(GetStackValueKind(method.Signature.ReturnType), "retval", returnValue, method.Signature.ReturnType); + PushExpression(GetStackValueKind(method.Signature.ReturnType), String.Empty, returnValue, method.Signature.ReturnType); } private void ImportCalli(int token) @@ -1020,10 +862,6 @@ 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 @@ -1033,10 +871,11 @@ namespace Internal.IL if (opcode == ILOpcode.brfalse || opcode == ILOpcode.brtrue) { var op = _stack.Pop(); - LLVMValueRef value = op.ValueAsInt32(_builder, false); - - if (LLVM.TypeOf(value).TypeKind != LLVMTypeKind.LLVMIntegerTypeKind) - throw new InvalidProgramException("branch on non integer"); + LLVMValueRef value = op.LLVMValue; + if (LLVM.GetTypeKind(LLVM.TypeOf(value)) == LLVMTypeKind.LLVMPointerTypeKind) + { + value = LLVM.BuildPtrToInt(_builder, value, LLVM.Int32Type(), String.Empty); + } if (opcode == ILOpcode.brfalse) { @@ -1064,8 +903,21 @@ namespace Internal.IL kind = op2.Kind; } - LLVMValueRef left = op1.ValueForStackKind(kind, _builder, false); - LLVMValueRef right = op2.ValueForStackKind(kind, _builder, false); + 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"); + } + } + switch (opcode) { @@ -1107,17 +959,19 @@ namespace Internal.IL if (target.StartOffset == 0) throw new NotImplementedException("cant branch to entry basic block"); - ImportFallthrough(target); - ImportFallthrough(fallthrough); LLVM.BuildCondBr(_builder, condition, GetLLVMBasicBlockForBlock(target), GetLLVMBasicBlockForBlock(fallthrough)); + + ImportFallthrough(fallthrough); } + + ImportFallthrough(target); } private void ImportSwitchJump(int jmpBase, int[] jmpDelta, BasicBlock fallthrough) { var operand = _stack.Pop(); - var @switch = LLVM.BuildSwitch(_builder, operand.ValueAsInt32(_builder, false), GetLLVMBasicBlockForBlock(fallthrough), (uint)jmpDelta.Length); + var @switch = LLVM.BuildSwitch(_builder, operand.LLVMValue, GetLLVMBasicBlockForBlock(fallthrough), (uint)jmpDelta.Length); for (var i = 0; i < jmpDelta.Length; i++) { var target = _basicBlocks[_currentOffset + jmpDelta[i]]; @@ -1135,13 +989,22 @@ namespace Internal.IL private void ImportLoadIndirect(TypeDesc 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())); + 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); } private void ImportStoreIndirect(int token) @@ -1153,18 +1016,29 @@ namespace Internal.IL { StackEntry value = _stack.Pop(); StackEntry destinationPointer = _stack.Pop(); - LLVMValueRef typedValue; - LLVMValueRef typedPointer; + LLVMTypeRef requestedPointerType = LLVM.PointerType(GetLLVMTypeForTypeDesc(type), 0); + LLVMValueRef typedValue = value.LLVMValue; + LLVMValueRef typedPointer = destinationPointer.LLVMValue; - if (type != null) + if (LLVM.GetTypeKind(LLVM.TypeOf(destinationPointer.LLVMValue)) != LLVMTypeKind.LLVMPointerTypeKind) { - typedValue = value.ValueAsType(type, _builder); - typedPointer = destinationPointer.ValueAsType(type.MakePointerType(), _builder); + typedPointer = LLVM.BuildIntToPtr(_builder, destinationPointer.LLVMValue, requestedPointerType, "stindintptrcast"); } else { - typedPointer = destinationPointer.ValueAsType(LLVM.PointerType(LLVM.Int32Type(), 0), _builder); - typedValue = value.ValueAsInt32(_builder, false); + 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"); + } } LLVM.BuildStore(_builder, typedValue, typedPointer); @@ -1191,140 +1065,106 @@ namespace Internal.IL } // The one exception from the above rule - if (kind == StackValueKind.ByRef) + if ((kind == StackValueKind.ByRef) && + (opcode == ILOpcode.sub || opcode == ILOpcode.sub_ovf || opcode == ILOpcode.sub_ovf_un)) { kind = StackValueKind.NativeInt; - type = type.MakePointerType(); + type = null; } LLVMValueRef result; - LLVMValueRef left = op1.ValueForStackKind(kind, _builder, false); - LLVMValueRef right = op2.ValueForStackKind(kind, _builder, false); - if (kind == StackValueKind.Float) + LLVMValueRef left = op1.LLVMValue; + LLVMValueRef right = op2.LLVMValue; + + if (kind == StackValueKind.NativeInt || kind == StackValueKind.ObjRef || kind == StackValueKind.ByRef) { - switch (opcode) + if (LLVM.GetTypeKind(LLVM.TypeOf(left)) == LLVMTypeKind.LLVMPointerTypeKind) { - 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 + left = LLVM.BuildPtrToInt(_builder, left, LLVM.Int32Type(), "lptrasint"); } - } - else - { - switch (opcode) + if (LLVM.GetTypeKind(LLVM.TypeOf(right)) == LLVMTypeKind.LLVMPointerTypeKind) { - 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 + right = LLVM.BuildPtrToInt(_builder, right, LLVM.Int32Type(), "rptrasint"); } } - - if (kind == StackValueKind.NativeInt || kind == StackValueKind.ByRef || kind == StackValueKind.ObjRef) + switch (opcode) { - //we need to put the type back if we changed it because it started out a pointer - result = CastToTypeDesc(result, type); + 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 } - PushExpression(kind, "binop", result, type); + + PushExpression(kind, "", 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, valueToShiftValue, numBitsToShift.ValueAsInt32(_builder, false), "shl"); + result = LLVM.BuildShl(_builder, valueToShift.LLVMValue, numBitsToShift.LLVMValue, "shl"); break; case ILOpcode.shr: - result = LLVM.BuildAShr(_builder, valueToShiftValue, numBitsToShift.ValueAsInt32(_builder, false), "shr"); + result = LLVM.BuildAShr(_builder, valueToShift.LLVMValue, numBitsToShift.LLVMValue, "shr"); break; case ILOpcode.shr_un: - result = LLVM.BuildLShr(_builder, valueToShiftValue, numBitsToShift.ValueAsInt32(_builder, false), "shr"); + result = LLVM.BuildLShr(_builder, valueToShift.LLVMValue, numBitsToShift.LLVMValue, "shr"); break; default: throw new InvalidOperationException(); // Should be unreachable } - PushExpression(valueToShift.Kind, "shiftop", result, valueToShift.Type); + PushExpression(valueToShift.Kind, "", result, valueToShift.Type); } private void ImportCompareOperation(ILOpcode opcode) @@ -1345,8 +1185,34 @@ namespace Internal.IL } LLVMValueRef result; - LLVMValueRef typeSaneOp1 = op1.ValueForStackKind(kind, _builder, true); - LLVMValueRef typeSaneOp2 = op2.ValueForStackKind(kind, _builder, true); + //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)); + } + } switch (opcode) { @@ -1369,30 +1235,23 @@ namespace Internal.IL throw new NotSupportedException(); // unreachable } - PushExpression(StackValueKind.Int32, "cmpop", result, GetWellKnownType(WellKnownType.SByte)); + PushExpression(kind, "", result, GetWellKnownType(WellKnownType.SByte)); } private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned) { StackEntry value = _stack.Pop(); - LLVMValueRef convertedValue; + StackEntry convertedValue = value.Duplicate(); //conv.u for a pointer should change to a int8* - if (wellKnownType == WellKnownType.UIntPtr) + if(wellKnownType == WellKnownType.UIntPtr) { if (value.Kind == StackValueKind.Int32) { - convertedValue = LLVM.BuildIntToPtr(_builder, value.ValueAsInt32(_builder, false), LLVM.PointerType(LLVM.Int8Type(), 0), "conv.u"); - } - else - { - convertedValue = value.ValueAsType(GetWellKnownType(wellKnownType), _builder); + convertedValue.LLVMValue = LLVM.BuildIntToPtr(_builder, value.LLVMValue, LLVM.PointerType(LLVM.Int8Type(), 0), "conv.u"); } } - else - { - convertedValue = value.ValueAsType(GetWellKnownType(wellKnownType), _builder); - } - PushExpression(value.Kind, "conv", convertedValue, value.Type); + + _stack.Push(convertedValue); } private void ImportUnaryOperation(ILOpcode opCode) @@ -1405,21 +1264,21 @@ namespace Internal.IL case ILOpcode.neg: if (argument.Kind == StackValueKind.Float) { - result = LLVM.BuildFNeg(_builder, argument.ValueForStackKind(argument.Kind, _builder, false), "neg"); + result = LLVM.BuildFNeg(_builder, argument.LLVMValue, "neg"); } else { - result = LLVM.BuildNeg(_builder, argument.ValueForStackKind(argument.Kind, _builder, true), "neg"); + result = LLVM.BuildNeg(_builder, argument.LLVMValue, "neg"); } break; case ILOpcode.not: - result = LLVM.BuildNot(_builder, argument.ValueForStackKind(argument.Kind, _builder, true), "not"); + result = LLVM.BuildNot(_builder, argument.LLVMValue, "not"); break; default: throw new NotSupportedException(); // unreachable } - PushExpression(argument.Kind, "unaryop", result, argument.Type); + PushExpression(argument.Kind, "", result, argument.Type); } private void ImportCpOpj(int token) @@ -1451,16 +1310,21 @@ namespace Internal.IL if (ldtokenValue is TypeDesc) { ldtokenKind = WellKnownType.RuntimeTypeHandle; - PushExpression(StackValueKind.ByRef, "ldtoken", GetEETypeForTypeDesc(ldtokenValue as TypeDesc), _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr")); + //AddTypeReference((TypeDesc)ldtokenValue, false); + + // todo: this doesn't work because we don't have the eetypeptr pushed. How do we get the eetypeptr? MethodDesc helper = _compilation.TypeSystemContext.GetHelperEntryPoint("LdTokenHelpers", "GetRuntimeTypeHandle"); - AddMethodReference(helper); + //AddMethodReference(helper); HandleCall(helper); name = ldtokenValue.ToString(); + + //value = new LdTokenEntry(StackValueKind.ValueType, name, (TypeDesc)ldtokenValue, GetWellKnownType(ldtokenKind)); } else if (ldtokenValue is FieldDesc) { ldtokenKind = WellKnownType.RuntimeFieldHandle; - value = new LdTokenEntry(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, GetWellKnownType(ldtokenKind)); + // todo: this is probably the wrong llvm value for the field + value = new LdTokenEntry(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, LLVM.ConstInt(LLVM.Int32Type(), (uint)token, LLVMMisc.False), GetWellKnownType(ldtokenKind)); _stack.Push(value); } else if (ldtokenValue is MethodDesc) @@ -1540,26 +1404,21 @@ namespace Internal.IL private LLVMValueRef GetInstanceFieldAddress(StackEntry objectEntry, FieldDesc field) { var objectType = objectEntry.Type ?? field.OwningType; - LLVMValueRef untypedObjectValue; - LLVMTypeRef llvmObjectType = GetLLVMTypeForTypeDesc(objectType); - if (objectEntry is LoadExpressionEntry) + LLVMValueRef typedObjectValue; + if (objectType.IsValueType && !objectType.IsPointer && objectEntry.Kind != StackValueKind.NativeInt && objectEntry.Kind != StackValueKind.ByRef) { - untypedObjectValue = CastToRawPointer(((LoadExpressionEntry)objectEntry).RawLLVMValue); - } - else if (objectType.IsValueType && !objectType.IsPointer && objectEntry.Kind != StackValueKind.NativeInt && objectEntry.Kind != StackValueKind.ByRef) - { - 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"); + typedObjectValue = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(objectType), "objptr"); + LLVM.BuildStore(_builder, objectEntry.LLVMValue, typedObjectValue); } else { - untypedObjectValue = objectEntry.ValueAsType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), _builder); + typedObjectValue = objectEntry.LLVMValue; } - var loadLocation = LLVM.BuildGEP(_builder, untypedObjectValue, + 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 loadLocation; + return LLVM.BuildPointerCast(_builder, loadLocation, LLVM.PointerType(GetLLVMTypeForTypeDesc(field.FieldType), 0), "fieldaddresscast"); } private LLVMValueRef GetFieldAddress(FieldDesc field, bool isStatic) @@ -1570,7 +1429,8 @@ namespace Internal.IL if (!isStatic) _stack.Pop(); - return WebAssemblyObjectWriter.EmitGlobal(Module, field, _compilation.NameMangler); + LLVMValueRef untypedFieldAddress = WebAssemblyObjectWriter.EmitGlobal(Module, field, _compilation.NameMangler); + return LLVM.BuildPointerCast(_builder, untypedFieldAddress, LLVM.PointerType(GetLLVMTypeForTypeDesc(field.FieldType), 0), "tempfieldaddresscast"); } else { @@ -1582,22 +1442,26 @@ namespace Internal.IL { FieldDesc field = (FieldDesc)_methodIL.GetObject(token); LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic); - PushLoadExpression(GetStackValueKind(field.FieldType), "ldfld_" + field.Name, fieldAddress, field.FieldType); + LLVMValueRef loadValue = LLVM.BuildLoad(_builder, fieldAddress, "ldfld_" + field.Name); + PushExpression(GetStackValueKind(field.FieldType), "ldfld", loadValue, field.FieldType); } private void ImportAddressOfField(int token, bool isStatic) { FieldDesc field = (FieldDesc)_methodIL.GetObject(token); LLVMValueRef fieldAddress = GetFieldAddress(field, isStatic); - _stack.Push(new AddressExpressionEntry(StackValueKind.ByRef, "ldflda", fieldAddress, field.FieldType.MakePointerType())); + PushExpression(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); - CastingStore(fieldAddress, valueEntry, field.FieldType); + LLVM.BuildStore(_builder, value, fieldAddress); } // Loads symbol address. Address is represented as a i32* @@ -1621,24 +1485,10 @@ 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) @@ -1723,48 +1573,38 @@ namespace Internal.IL if (_stack.Length > 0) { entryStack = new EvaluationStack(_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++) { - entryStack.Push(NewSpillSlot(_stack[i])); + // todo: do we need anything special for spilled stacks like cpp codegen does? + entryStack.Push(_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++) { - 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)); + /*AppendLine(); + Append(entryStack[i]); + Append(" = "); + Append(_stack[i]); + AppendSemicolon();*/ } +#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 3a5f48117..8a814508e 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter_Statics.cs @@ -101,6 +101,7 @@ namespace Internal.IL static LLVMValueRef TrapFunction = default(LLVMValueRef); static LLVMValueRef DoNothingFunction = default(LLVMValueRef); + private static IEnumerable 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 140e2b1bc..335157917 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs @@ -188,17 +188,6 @@ 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) @@ -214,7 +203,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, GetConstZeroArray(field.FieldType.GetElementSize().AsInt)); + LLVM.SetInitializer(llvmValue, LLVM.ConstPointerNull(valueType)); s_staticFieldMapping.Add(field, llvmValue); return llvmValue; } @@ -273,7 +262,7 @@ namespace ILCompiler.DependencyAnalysis { LLVMValueRef valRef = IsFunction ? LLVM.GetNamedFunction(module, SymbolName) : LLVM.GetNamedGlobal(module, SymbolName); - if (Offset != 0 && valRef.Pointer != IntPtr.Zero) + if (Offset != 0) { var pointerType = LLVM.PointerType(LLVM.Int8Type(), 0); var bitCast = LLVM.ConstBitCast(valRef, pointerType); @@ -324,16 +313,8 @@ namespace ILCompiler.DependencyAnalysis if (ObjectSymbolRefs.TryGetValue(curOffset, out symbolRef)) { LLVMValueRef pointedAtValue = symbolRef.ToLLVMValueRef(module); - //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)); - } + var ptrValue = LLVM.ConstBitCast(pointedAtValue, intPtrType); + entries.Add(ptrValue); } else { @@ -377,6 +358,7 @@ 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 978e29f8e..039969de2 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 unstubbed node - return new WebAssemblyMethodCodeNode(method); + // TODO: this is wrong: this returns an assembly stub node + return new UnboxingStubNode(method, Target); } protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall) diff --git a/tests/src/Simple/HelloWasm/Program.cs b/tests/src/Simple/HelloWasm/Program.cs index 2cb0eb895..453a8555e 100644 --- a/tests/src/Simple/HelloWasm/Program.cs +++ b/tests/src/Simple/HelloWasm/Program.cs @@ -11,12 +11,10 @@ internal static class Program private static unsafe void Main(string[] args) { Add(1, 2); - var tempObj = new TestClass(1337); + int tempInt = 0; (*(&tempInt)) = 9; - tempObj.TestMethod("Hello"); - if(tempInt == 9) { PrintLine("Hello from C#!"); @@ -24,8 +22,6 @@ internal static class Program TwoByteStr str = new TwoByteStr() { first = 1, second = 2 }; TwoByteStr str2 = new TwoByteStr() { first = 3, second = 4 }; - *(&str) = str2; - str2 = *(&str); if (str2.second == 4) { @@ -61,7 +57,6 @@ internal static class Program { PrintLine("shiftRight test: Ok."); } - var unsignedShift = UnsignedShift(0xFFFFFFFFu, 4) == 0x0FFFFFFFu; if (unsignedShift) { @@ -164,21 +159,3 @@ public struct TwoByteStr public byte second; } -public class TestClass -{ - public string TestString {get; set;} - - public TestClass(int number) - { - if(number != 1337) - throw new Exception(); - } - - public void TestMethod(string str) - { - TestString = str; - if(TestString != str) - throw new Exception(); - } -} - -- cgit v1.2.3 From aff9adfef2c32f8b2d96d5bc3bb653f4925080ce Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 31 Oct 2017 08:30:29 -0400 Subject: Merge pull request dotnet/coreclr#14759 from stephentoub/gettaskforresult_inlining Don't inline GetTaskForResult when await'ing ValueTask Signed-off-by: dotnet-bot --- .../Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs | 4 ++++ .../shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs | 4 ++++ .../shared/System/Threading/Tasks/ValueTask.cs | 9 +++++++++ 3 files changed, 17 insertions(+) diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs index 071911813..06e1e1f57 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -71,6 +71,10 @@ namespace System.Runtime.CompilerServices /// Gets the task underlying . internal Task AsTask() => _value.AsTask(); + + /// Gets the task underlying the incomplete . + /// This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task. + (Task, bool) IConfiguredValueTaskAwaiter.GetTask() => (_value.AsTaskExpectNonNull(), _continueOnCapturedContext); } } } diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs index 203039a4a..3ce800139 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -38,5 +38,9 @@ namespace System.Runtime.CompilerServices /// Gets the task underlying . internal Task AsTask() => _value.AsTask(); + + /// Gets the task underlying the incomplete . + /// This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task. + Task IValueTaskAwaiter.GetTask() => _value.AsTaskExpectNonNull(); } } diff --git a/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs b/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs index 384e4a8ab..4ccf0f8a4 100644 --- a/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs @@ -116,6 +116,15 @@ namespace System.Threading.Tasks // and the hash code we generate in GetHashCode. _task ?? AsyncTaskMethodBuilder.GetTaskForResult(_result); + internal Task AsTaskExpectNonNull() => + // Return the task if we were constructed from one, otherwise manufacture one. + // Unlike AsTask(), this method is called only when we expect _task to be non-null, + // and thus we don't want GetTaskForResult inlined. + _task ?? GetTaskForResultNoInlining(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private Task GetTaskForResultNoInlining() => AsyncTaskMethodBuilder.GetTaskForResult(_result); + /// Gets whether the represents a completed operation. public bool IsCompleted => _task == null || _task.IsCompleted; -- cgit v1.2.3 From 9b892d1b796f3d24d792a299fa12c2c3a22b7456 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 2 Nov 2017 20:14:32 -0400 Subject: Move I{Configured}ValueTaskAwaiter interfaces to correct location They need to be in the shared partition. --- .../Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs | 8 ++++++++ .../shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs index 06e1e1f57..ead4b524d 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -77,4 +77,12 @@ namespace System.Runtime.CompilerServices (Task, bool) IConfiguredValueTaskAwaiter.GetTask() => (_value.AsTaskExpectNonNull(), _continueOnCapturedContext); } } + + /// + /// Internal interface used to enable extract the Task from arbitrary configured ValueTask awaiters. + /// + internal interface IConfiguredValueTaskAwaiter + { + (Task task, bool continueOnCapturedContext) GetTask(); + } } diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs index 3ce800139..40a7fe63f 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -43,4 +43,12 @@ namespace System.Runtime.CompilerServices /// This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task. Task IValueTaskAwaiter.GetTask() => _value.AsTaskExpectNonNull(); } + + /// + /// Internal interface used to enable extract the Task from arbitrary ValueTask awaiters. + /// > + internal interface IValueTaskAwaiter + { + Task GetTask(); + } } -- cgit v1.2.3 From 50c7b8b4ced61a74fa5804f35418932e1c46d990 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Tue, 24 Oct 2017 14:39:29 -0700 Subject: Update code in AwaitUnsafeOnCompleted to use interface matching Given that the jit can now avoid boxing on some interface calls to value types, generalize the patterns introduced in AwaitUnsafeOnCompleted in #14718 by using interfaces instead of checking for specific types. Also move the catch-all processing back in line as the jit can now fold the interface tests early and not pull in the EH portion of the method unless it is needed. --- .../System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs | 2 +- .../shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs index ead4b524d..fa3e7c06c 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -35,7 +35,7 @@ namespace System.Runtime.CompilerServices /// Provides an awaiter for a . [StructLayout(LayoutKind.Auto)] - public struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion + public struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, IConfiguredValueTaskAwaiter { /// The value being awaited. private ValueTask _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs index 40a7fe63f..0b0ed8595 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace System.Runtime.CompilerServices { /// Provides an awaiter for a . - public struct ValueTaskAwaiter : ICriticalNotifyCompletion + public struct ValueTaskAwaiter : ICriticalNotifyCompletion, IValueTaskAwaiter { /// The value being awaited. private ValueTask _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies -- cgit v1.2.3 From efb22913e3d11b3aef192d655ee53b954912dbe2 Mon Sep 17 00:00:00 2001 From: Aditya Mandaleeka Date: Fri, 3 Nov 2017 13:42:57 -0700 Subject: Use libunwind more efficiently to speed up finding method info. --- src/Native/Runtime/unix/UnixContext.cpp | 53 ++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/Native/Runtime/unix/UnixContext.cpp b/src/Native/Runtime/unix/UnixContext.cpp index 3be92658f..c2beadd5c 100644 --- a/src/Native/Runtime/unix/UnixContext.cpp +++ b/src/Native/Runtime/unix/UnixContext.cpp @@ -257,6 +257,43 @@ static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *curso #endif // _AMD64_ } +// Returns the unw_proc_info_t for a given IP. +bool GetUnwindProcInfo(PCODE ip, unw_proc_info_t *procInfo) +{ + int st; + + unw_context_t unwContext; + unw_cursor_t cursor; + + st = unw_getcontext(&unwContext); + if (st < 0) + { + return false; + } + +#ifdef _AMD64_ + unwContext.data[16] = ip; +#elif _ARM_ + unwContext.data[15] = ip; +#else + #error "GetUnwindProcInfo is not supported on this arch yet." +#endif + + st = unw_init_local(&cursor, &unwContext); + if (st < 0) + { + return false; + } + + st = unw_get_proc_info(&cursor, procInfo); + if (st < 0) + { + return false; + } + + return true; +} + // Initialize unw_cursor_t and unw_context_t from REGDISPLAY bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* cursor, unw_context_t* unwContext) { @@ -516,21 +553,9 @@ uint64_t GetPC(void* context) // Find LSDA and start address for a function at address controlPC bool FindProcInfo(UIntNative controlPC, UIntNative* startAddress, UIntNative* lsda) { - unw_context_t unwContext; - unw_cursor_t cursor; - REGDISPLAY regDisplay; - memset(®Display, 0, sizeof(REGDISPLAY)); - - regDisplay.SetIP((PCODE)controlPC); - - if (!InitializeUnwindContextAndCursor(®Display, &cursor, &unwContext)) - { - return false; - } - unw_proc_info_t procInfo; - int st = unw_get_proc_info(&cursor, &procInfo); - if (st < 0) + + if (!GetUnwindProcInfo((PCODE)controlPC, &procInfo)) { return false; } -- cgit v1.2.3 From 9e2457d878ca440647b771e99cbf2e8dbacd9ed1 Mon Sep 17 00:00:00 2001 From: Alexander Soldatov Date: Fri, 3 Nov 2017 23:47:48 +0300 Subject: [armel][cross] Documentation how to use cross compilation (#4865) Added documentations and fixed few typos --- Documentation/cross-building.md | 69 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/Documentation/cross-building.md b/Documentation/cross-building.md index afc0f07c9..247f933ac 100644 --- a/Documentation/cross-building.md +++ b/Documentation/cross-building.md @@ -1,7 +1,7 @@ Cross Compilation for ARM on Linux ================================== -Through cross compilation, on Linux it is possible to build CoreCLR for arm or arm64. +Through cross compilation, on Linux it is possible to build CoreRT for arm or arm64. Requirements ------------ @@ -21,7 +21,7 @@ and conversely for arm64: Generating the rootfs --------------------- -The `cross\build-rootfs.sh` script can be used to download the files needed for cross compilation. It will generate an Ubuntu 14.04 rootfs as this is what CoreCLR targets. +The `cross\build-rootfs.sh` script can be used to download the files needed for cross compilation. It will generate an Ubuntu 14.04 rootfs as this is what CoreRT targets. Usage: build-rootfs.sh [BuildArch] BuildArch can be: arm, arm64 @@ -38,7 +38,7 @@ and if you wanted to generate the rootfs elsewhere: Cross compiling CoreCLR ----------------------- -Once the rootfs has been generated, it will be possible to cross compile CoreCLR. If `ROOTFS_DIR` was set when generating the rootfs, then it must also be set when running `build.sh`. +Once the rootfs has been generated, it will be possible to cross compile CoreRT. If `ROOTFS_DIR` was set when generating the rootfs, then it must also be set when running `build.sh`. So, without `ROOTFS_DIR`: @@ -49,3 +49,66 @@ And with: $ ROOTFS_DIR=~/coreclr-cross/arm ./build.sh arm debug verbose clean cross As usual the resulting binaries will be found in `bin/Product/BuildOS.BuildArch.BuildType/` + +Using CoreRT for cross compiling under arm on x86 host +----------------------- +It is possible to use CoreRT for compiling under arm/armel on x86 host (or on x64 machine using roots). + +1. Build CoreCLR for x86 (`checked` version) +``` +sudo ./cross/build-rootfs.sh x86 xenial +./build.sh clang3.9 x86 checked verbose cross skiptests +``` + +2. Build CoreFX (`Debug` version) +3. Build CoreRT for armel, x64, x86 +``` +sudo ./cross/build-rootfs.sh armel tizen +sudo ./cross/build-rootfs.sh x86 xenial +./build.sh clang3.9 armel debug verbose cross +./build.sh debug verbose skiptests +./build.sh clang3.9 x86 debug verbose cross skiptests +``` + +4. Copy necessary binaries to working directory (in x86 rootfs) +``` +cp ${CORECLR}/bin/Product/Linux.x86.Checked ${WORKING_DIR} +cp ${CORERT}/bin/Linux.x86.Debug/tools/ilc.dll ${WORKING_DIR} +cp ${CORERT}/bin/Linux.x86.Debug/tools/ILCompiler.* ${WORKING_DIR} +cp ${CORERT}/bin/Linux.x86.Debug/tools/System.CommandLine.dll ${WORKING_DIR} +cp ${CORERT}/bin/Linux.x86.Debug/tools/Microsoft.DiaSymReader.dll ${WORKING_DIR} +cp ${CORERT}/bin/Linux.x86.Debug/tools/jitinterface.so ${WORKING_DIR} +cp -r ${CORERT}/bin/Linux.x86.Debug/framework ${WORKING_DIR} + +# Copy CoreRT sdk binaries from target (armel) output folder +cp -r ${CORERT}/bin/Linux.armel.Debug/sdk ${WORKING_DIR} +``` + +5. Rename RyuJIT compiler library +``` +# Use cross-compiler library as default for ILC +cp ${WORKING_DIR}/libarmelnonjit.so ${WORKING_DIR}/libclrjitilc.so + +# ... or ARM version instead if it's needed +# cp ${WORKING_DIR}/libprotojit.so ${WORKING_DIR}/libclrjitilc.so +``` + +6. Build libobjwriter. You have to compile it on x86 chroot. Before compiling put coreclr/bin/Product/Linux.x86.Debug/ to some folder on x86 chroot as well. Versions which to used are mentioned on GitHub: +https://github.com/dotnet/corert/issues/3776#issuecomment-337682166 + + And apply patch: +https://gist.github.com/alpencolt/ec75fcc05d8c4ffbf143a052f7c115a8 +``` +mkdir build +cd build +cmake ../ -DWITH_CORECLR=../../coreclr/bin/Product/Linux.x86.Debug/ -DLLVM_TARGET_ARCH="ARM;X86" -DLLVM_TARGETS_TO_BUILD="ARM;X86" -DLLVM_DEFAULT_TARGET_TRIPLE=thumbv7-linux-gnueabi -DCMAKE_BUILD_TYPE=Release -DLLVM_BUILD_LLVM_DYLIB=1 -DLLVM_LINK_LLVM_DYLIB=1 -DLLVM_OPTIMIZED_TABLEGEN=1 -DHAVE_POSIX_SPAWN=0 -DLLVM_ENABLE_PIC=1 -DLLVM_BUILD_TESTS=0 -DLLVM_ENABLE_DOXYGEN=0 -DLLVM_INCLUDE_DOCS=0 -DLLVM_INCLUDE_TESTS=0 -DLLVM_BINUTILS_INCDIR=/usr/include +make -j8 objwriter +``` + +7. And to execute use: +``` +./corerun ilc.dll --verbose @Hello.ilc.rsp + +# For linking +clang-3.9 -target arm-linux-gnueabi --sysroot=corert/cross/rootfs/armel -Bcorert/cross/rootfs/armel/usr/lib/gcc/armv7l-tizen-linux-gnueabi/6.2.1 -Lcorert/cross/rootfs/armel/usr/lib/gcc/armv7l-tizen-linux-gnueabi/6.2.1 Hello.o -o Hello corert/bin/Linux.armel.Debug/sdk/libbootstrapper.a corert/bin/Linux.armel.Debug/sdk/libRuntime.a corert/bin/Linux.armel.Debug/sdk/libSystem.Private.CoreLib.Native.a corert/bin/Linux.armel.Debug/framework/System.Native.a corert/bin/Linux.armel.Debug/framework/libSystem.Globalization.Native.a -g -Wl,-rpath,'$ORIGIN' -pthread -lstdc++ -ldl -lm -luuid -lrt -fPIC +``` -- cgit v1.2.3 From d5f460cab90878d8259231540e1be4d6246ef92b Mon Sep 17 00:00:00 2001 From: Aditya Mandaleeka Date: Fri, 3 Nov 2017 15:33:54 -0700 Subject: Add comment about indexing into unw_context_t. --- src/Native/Runtime/unix/UnixContext.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Native/Runtime/unix/UnixContext.cpp b/src/Native/Runtime/unix/UnixContext.cpp index c2beadd5c..fcf8b41dc 100644 --- a/src/Native/Runtime/unix/UnixContext.cpp +++ b/src/Native/Runtime/unix/UnixContext.cpp @@ -272,6 +272,9 @@ bool GetUnwindProcInfo(PCODE ip, unw_proc_info_t *procInfo) } #ifdef _AMD64_ + // We manually index into the unw_context_t's internals for now because there's + // no better way to modify it. This will go away in the future when we locate the + // LSDA and other information without initializing an unwind cursor. unwContext.data[16] = ip; #elif _ARM_ unwContext.data[15] = ip; -- cgit v1.2.3 From 0666ff40fb14850eab7e1d055b60b57fcb976183 Mon Sep 17 00:00:00 2001 From: Aditya Mandaleeka Date: Thu, 2 Nov 2017 19:45:22 -0700 Subject: Improve unwinder initialization perf. --- src/Native/Runtime/unix/UnixContext.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Native/Runtime/unix/UnixContext.cpp b/src/Native/Runtime/unix/UnixContext.cpp index fcf8b41dc..1d1324cfb 100644 --- a/src/Native/Runtime/unix/UnixContext.cpp +++ b/src/Native/Runtime/unix/UnixContext.cpp @@ -233,7 +233,7 @@ static void RegDisplayToUnwindContext(REGDISPLAY* regDisplay, unw_context_t *unw } // Update unw_cursor_t from REGDISPLAY -static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *cursor) +static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *cursor, bool setIp) { #if defined(_AMD64_) #define ASSIGN_REG(regName1, regName2) \ @@ -243,7 +243,11 @@ static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *curso if (regDisplay->p##regName2 != NULL) \ unw_set_reg(cursor, regName1, *(regDisplay->p##regName2)); - ASSIGN_REG(UNW_REG_IP, IP) + if (setIp) + { + ASSIGN_REG(UNW_REG_IP, IP) + } + ASSIGN_REG(UNW_REG_SP, SP) ASSIGN_REG_PTR(UNW_X86_64_RBP, Rbp) ASSIGN_REG_PTR(UNW_X86_64_RBX, Rbx) @@ -308,7 +312,18 @@ bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* curs return false; } + bool ipSetInUnwindContext = true; + +#ifdef _AMD64_ + // We manually index into the unw_context_t's internals for now because there's + // no better way to modify it. This whole function will go away in the future + // when we are able to read unwind info without initializing an unwind cursor. + unwContext->data[16] = regDisplay->IP; +#elif _ARM_ RegDisplayToUnwindContext(regDisplay, unwContext); +#else + ipSetInUnwindContext = false; +#endif st = unw_init_local(cursor, unwContext); if (st < 0) @@ -316,8 +331,11 @@ bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* curs return false; } - // Set the unwind context to the specified windows context - RegDisplayToUnwindCursor(regDisplay, cursor); + // Set the unwind context to the specified Windows context. + // We skip the IP register if it was already set in the unw_context + // passed into during unw_init_local. Setting it again is not necessary + // and causes libunwind to do extra work. + RegDisplayToUnwindCursor(regDisplay, cursor, !ipSetInUnwindContext /* setIp */); return true; } -- cgit v1.2.3 From 5d5c426dde675be3cc2d27ca7d1af374da7c2e66 Mon Sep 17 00:00:00 2001 From: Aditya Mandaleeka Date: Fri, 3 Nov 2017 19:48:02 -0700 Subject: Remove ARM workaround used for nongnu libunwind. --- src/Native/Runtime/unix/UnixContext.cpp | 37 +-------------------------------- 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/src/Native/Runtime/unix/UnixContext.cpp b/src/Native/Runtime/unix/UnixContext.cpp index 1d1324cfb..02d832b4c 100644 --- a/src/Native/Runtime/unix/UnixContext.cpp +++ b/src/Native/Runtime/unix/UnixContext.cpp @@ -197,41 +197,6 @@ #endif // __APPLE__ -// Update unw_context_t from REGDISPLAY -static void RegDisplayToUnwindContext(REGDISPLAY* regDisplay, unw_context_t *unwContext) -{ -#if defined(_ARM_) - // Assuming that unw_set_reg() on cursor will point the cursor to the - // supposed stack frame is dangerous for libunwind-arm in Linux. - // It is because libunwind's unw_cursor_t has other data structure - // initialized by unw_init_local(), which are not updated by - // unw_set_reg(). - -#define ASSIGN_REG(regIndex, regName) \ - unwContext->data[regIndex] = (regDisplay->regName); - -#define ASSIGN_REG_PTR(regIndex, regName) \ - if (regDisplay->p##regName != NULL) \ - unwContext->data[regIndex] = *(regDisplay->p##regName); - - ASSIGN_REG_PTR(4, R4); - ASSIGN_REG_PTR(5, R5); - ASSIGN_REG_PTR(6, R6); - ASSIGN_REG_PTR(7, R7); - ASSIGN_REG_PTR(8, R8); - ASSIGN_REG_PTR(9, R9); - ASSIGN_REG_PTR(10, R10); - ASSIGN_REG_PTR(11, R11); - ASSIGN_REG(13, SP); - ASSIGN_REG_PTR(14, LR); - ASSIGN_REG(15, IP); - -#undef ASSIGN_REG -#undef ASSIGN_REG_PTR - -#endif // _ARM_ -} - // Update unw_cursor_t from REGDISPLAY static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *cursor, bool setIp) { @@ -320,7 +285,7 @@ bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* curs // when we are able to read unwind info without initializing an unwind cursor. unwContext->data[16] = regDisplay->IP; #elif _ARM_ - RegDisplayToUnwindContext(regDisplay, unwContext); + unwContext.data[15] = regDisplay->IP; #else ipSetInUnwindContext = false; #endif -- cgit v1.2.3 From ff7decc2fdfc22c32c627734c2ff4eda2696911b Mon Sep 17 00:00:00 2001 From: Jeff Greene Date: Sat, 4 Nov 2017 16:55:48 -0700 Subject: implement newobj for WASM using malloc (#4872) implemented newobj for WASM using malloc --- .../src/CodeGen/EvaluationStack.cs | 251 ++++--- .../src/CodeGen/ILToWebAssemblyImporter.cs | 773 +++++++++++++-------- .../src/CodeGen/ILToWebAssemblyImporter_Statics.cs | 1 - .../src/CodeGen/WebAssemblyObjectWriter.cs | 28 +- .../WebAssemblyCodegenNodeFactory.cs | 4 +- tests/src/Simple/HelloWasm/Program.cs | 25 +- 6 files changed, 694 insertions(+), 388 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 /// Position where to insert 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 /// 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); /// /// Initializes a new instance of StackEntry. /// /// Kind of entry. /// Type if any of entry. - protected StackEntry(StackValueKind kind, LLVMValueRef llvmValue, TypeDesc type = null) + protected StackEntry(StackValueKind kind, TypeDesc type = null) { Kind = kind; Type = type; - LLVMValue = llvmValue; } /// @@ -206,45 +240,6 @@ namespace Internal.IL /// /// A new instance of the same type as the current entry. public abstract StackEntry Duplicate(); - - /// - /// Overridden and sealed to force descendants to override . - /// - /// String representation of current entry - public override sealed string ToString() - { - StringBuilder s = new StringBuilder(); - BuildRepresentation(s); - return s.ToString(); - } - - /// - /// Build a representation of current entry in . - /// - /// StringBuilder where representation will be saved. - 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(')'); - } - } - } } /// @@ -252,7 +247,7 @@ namespace Internal.IL /// 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 { - 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 { - 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 { - 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 /// public string Name { get; set; } - + public LLVMValueRef RawLLVMValue { get; set; } /// /// Initializes new instance of ExpressionEntry /// /// Kind of entry /// String representation of entry /// Type if any of entry - 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 + { + /// + /// Initializes new instance of ExpressionEntry + /// + /// Kind of entry + /// String representation of entry + /// Type if any of entry + 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 + { + /// + /// Initializes new instance of ExpressionEntry + /// + /// Kind of entry + /// String representation of entry + /// Type if any of entry + 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); } } - + /// /// Entry representing some token (either of TypeDesc, MethodDesc or FieldDesc) along with its string representation /// @@ -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(Kind, Name, LdToken, LLVMValue, Type); + return new LdTokenEntry(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 /// 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(); + } + } + + /// + /// Entry representing a writable sharable stack entry that can survive from one basic block to another + /// + 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 _dependencies = new ArrayBuilder(); public IEnumerable GetDependencies() { @@ -35,6 +42,7 @@ namespace Internal.IL private LLVMBasicBlockRef _curBasicBlock; private LLVMBuilderRef _builder; private readonly LocalVariableDefinition[] _locals; + private List _spilledExpressions = new List(); 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)); + } + /// /// Push an expression named of kind . /// @@ -194,45 +226,6 @@ namespace Internal.IL _stack.Push(new ExpressionEntry(kind, name, llvmValue, type)); } - - - /// - /// Generate a cast in case the stack type of source is not identical or compatible with destination type. - /// - /// Type of destination - /// Source entry from stack - 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])); } } @@ -303,14 +298,73 @@ 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,7 +470,33 @@ 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) @@ -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: @@ -520,6 +612,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"); + } + /// /// Implements intrinsic methods instread of calling them /// @@ -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(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(StackValueKind.ValueType, null, (FieldDesc)ldtokenValue, LLVM.ConstInt(LLVM.Int32Type(), (uint)token, LLVMMisc.False), GetWellKnownType(ldtokenKind)); + value = new LdTokenEntry(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(_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 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) diff --git a/tests/src/Simple/HelloWasm/Program.cs b/tests/src/Simple/HelloWasm/Program.cs index 453a8555e..2cb0eb895 100644 --- a/tests/src/Simple/HelloWasm/Program.cs +++ b/tests/src/Simple/HelloWasm/Program.cs @@ -11,10 +11,12 @@ internal static class Program private static unsafe void Main(string[] args) { Add(1, 2); - + var tempObj = new TestClass(1337); int tempInt = 0; (*(&tempInt)) = 9; + tempObj.TestMethod("Hello"); + if(tempInt == 9) { PrintLine("Hello from C#!"); @@ -22,6 +24,8 @@ internal static class Program TwoByteStr str = new TwoByteStr() { first = 1, second = 2 }; TwoByteStr str2 = new TwoByteStr() { first = 3, second = 4 }; + *(&str) = str2; + str2 = *(&str); if (str2.second == 4) { @@ -57,6 +61,7 @@ internal static class Program { PrintLine("shiftRight test: Ok."); } + var unsignedShift = UnsignedShift(0xFFFFFFFFu, 4) == 0x0FFFFFFFu; if (unsignedShift) { @@ -159,3 +164,21 @@ public struct TwoByteStr public byte second; } +public class TestClass +{ + public string TestString {get; set;} + + public TestClass(int number) + { + if(number != 1337) + throw new Exception(); + } + + public void TestMethod(string str) + { + TestString = str; + if(TestString != str) + throw new Exception(); + } +} + -- cgit v1.2.3 From 9a819d14db26b678e8fd9338084abd1665b18148 Mon Sep 17 00:00:00 2001 From: Jeff Greene Date: Mon, 6 Nov 2017 00:23:49 -0800 Subject: added support for box/unbox/unbox_any for WASM using malloc (#4731) --- .../src/CodeGen/ILToWebAssemblyImporter.cs | 57 +++++++++++++++++++++- tests/src/Simple/HelloWasm/Program.cs | 8 ++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs index ebe4a44bf..745dd0d20 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs @@ -804,6 +804,11 @@ namespace Internal.IL { MetadataType metadataType = (MetadataType)type; int objectSize = metadataType.InstanceByteCount.AsInt; + if (metadataType.IsValueType) + { + objectSize += type.Context.Target.PointerSize; + } + 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); @@ -1428,6 +1433,37 @@ namespace Internal.IL private void ImportUnbox(int token, ILOpcode opCode) { + TypeDesc type = ResolveTypeToken(token); + if (type.IsNullable) + throw new NotImplementedException(); + + if (opCode == ILOpcode.unbox) + { + var unboxResult = _stack.Pop().ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder); + LLVMValueRef unboxData = LLVM.BuildGEP(_builder, unboxResult, new LLVMValueRef[] { BuildConstInt32(type.Context.Target.PointerSize) }, "unboxData"); + //push the pointer to the data, but it shouldnt be implicitly dereferenced + PushExpression(GetStackValueKind(type), "unboxed", unboxData, type); + } + else //unbox_any + { + Debug.Assert(opCode == ILOpcode.unbox_any); + + //TODO: when the runtime is ready switch this to calling the real RhUnboxAny + //LLVMValueRef eeType = GetEETypeForTypeDesc(type); + //var eeTypeDesc = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "EETypePtr"); + //LLVMValueRef untypedObjectValue = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(type), "objptr"); + //PushExpression(StackValueKind.ByRef, "objPtr", untypedObjectValue, type.MakePointerType()); + //PushExpression(StackValueKind.ByRef, "eeType", eeType, eeTypeDesc); + //CallRuntimeExport(_compilation.TypeSystemContext, "RhUnboxAny"); + //PushLoadExpression(GetStackValueKind(type), "unboxed", untypedObjectValue, type); + //this can be removed once we can call RhUnboxAny + if (!type.IsValueType) + throw new NotImplementedException(); + + var unboxResult = _stack.Pop().ValueAsType(LLVM.PointerType(LLVM.Int8Type(), 0), _builder); + LLVMValueRef unboxData = LLVM.BuildGEP(_builder, unboxResult, new LLVMValueRef[] { BuildConstInt32(type.Context.Target.PointerSize) }, "unboxData"); + PushLoadExpression(GetStackValueKind(type), "unboxed", unboxData, type); + } } private void ImportRefAnyVal(int token) @@ -1542,7 +1578,6 @@ namespace Internal.IL var objectType = objectEntry.Type ?? field.OwningType; LLVMValueRef untypedObjectValue; LLVMTypeRef llvmObjectType = GetLLVMTypeForTypeDesc(objectType); - if (objectType.IsValueType && !objectType.IsPointer && objectEntry.Kind != StackValueKind.NativeInt && objectEntry.Kind != StackValueKind.ByRef) { if (objectEntry is LoadExpressionEntry) @@ -1560,7 +1595,6 @@ namespace Internal.IL { untypedObjectValue = objectEntry.ValueAsType(LLVM.PointerType(LLVMTypeRef.Int8Type(), 0), _builder); } - if (field.Offset.AsInt == 0) { return untypedObjectValue; @@ -1649,7 +1683,19 @@ namespace Internal.IL private void ImportBox(int token) { + TypeDesc type = ResolveTypeToken(token); + if (type.IsValueType) + { + if (type.IsNullable) + throw new NotImplementedException(); + var value = _stack.Pop(); + ExpressionEntry boxTarget = AllocateObject(type); + LLVMValueRef boxData = LLVM.BuildGEP(_builder, boxTarget.RawLLVMValue, new LLVMValueRef[] { BuildConstInt32(type.Context.Target.PointerSize) }, "boxData"); + LLVMValueRef typedBoxData = LLVM.BuildPointerCast(_builder, boxData, LLVM.PointerType(GetLLVMTypeForTypeDesc(type), 0), "typedBoxData"); + LLVM.BuildStore(_builder, value.ValueAsType(type, _builder), typedBoxData); + _stack.Push(boxTarget); + } } private void ImportLeave(BasicBlock target) @@ -1762,6 +1808,13 @@ namespace Internal.IL } + private void CallRuntimeExport(TypeSystemContext context, string methodName) + { + MetadataType helperType = context.SystemModule.GetKnownType("System.Runtime", "RuntimeExports"); + MethodDesc helperMethod = helperType.GetKnownMethod(methodName, null); + HandleCall(helperMethod); + } + private StackEntry NewSpillSlot(StackEntry entry) { if (entry is SpilledExpressionEntry) diff --git a/tests/src/Simple/HelloWasm/Program.cs b/tests/src/Simple/HelloWasm/Program.cs index 2cb0eb895..6d4a5a6e7 100644 --- a/tests/src/Simple/HelloWasm/Program.cs +++ b/tests/src/Simple/HelloWasm/Program.cs @@ -38,6 +38,12 @@ internal static class Program PrintLine("static int field test: Ok."); } + var boxedInt = (object)tempInt; + if(((int)boxedInt) == 9) + { + PrintLine("box test: Ok."); + } + var not = Not(0xFFFFFFFF) == 0x00000000; if (not) { @@ -167,7 +173,7 @@ public struct TwoByteStr public class TestClass { public string TestString {get; set;} - + public TestClass(int number) { if(number != 1337) -- cgit v1.2.3 From 89d1b0a1578d7956c2df41570b74f35d6b91ad86 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 6 Nov 2017 17:37:00 -0500 Subject: Fix TryParse overloads using optional arguments (#4877) --- src/System.Private.CoreLib/src/System/Decimal.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/System.Private.CoreLib/src/System/Decimal.cs b/src/System.Private.CoreLib/src/System/Decimal.cs index 9ee8069f3..ad5709161 100644 --- a/src/System.Private.CoreLib/src/System/Decimal.cs +++ b/src/System.Private.CoreLib/src/System/Decimal.cs @@ -556,6 +556,11 @@ namespace System return Number.TryParseDecimal(s.AsReadOnlySpan(), NumberStyles.Number, null, out result); } + public static bool TryParse(ReadOnlySpan s, out decimal result) + { + return Number.TryParseDecimal(s, NumberStyles.Number, null, out result); + } + public static Boolean TryParse(String s, NumberStyles style, IFormatProvider provider, out Decimal result) { ValidateParseStyleFloatingPoint(style); @@ -567,7 +572,7 @@ namespace System return Number.TryParseDecimal(s.AsReadOnlySpan(), style, provider, out result); } - public static Boolean TryParse(ReadOnlySpan s, out Decimal result, NumberStyles style = NumberStyles.Integer, IFormatProvider provider = null) + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out decimal result) { ValidateParseStyleFloatingPoint(style); return Number.TryParseDecimal(s, style, provider, out result); -- cgit v1.2.3 From f47cd5524b3cb0f2b41f8cc70c15199f24111ef0 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 2 Nov 2017 09:22:19 -0700 Subject: Move Dictionary to shared CoreLib partition (dotnet/coreclr#14795) Signed-off-by: dotnet-bot --- .../shared/System.Private.CoreLib.Shared.projitems | 3 + .../System/Collections/Generic/Dictionary.cs | 1463 ++++++++++++++++++++ .../Generic/NonRandomizedStringEqualityComparer.cs | 38 + .../shared/System/Collections/HashHelpers.cs | 108 ++ .../src/System.Private.CoreLib.csproj | 5 - .../src/System/Collections/Generic/Dictionary.cs | 1447 ------------------- .../Generic/NonRandomizedStringEqualityComparer.cs | 30 - .../src/System/ThrowHelper.cs | 74 +- 8 files changed, 1674 insertions(+), 1494 deletions(-) create mode 100644 src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs create mode 100644 src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs create mode 100644 src/System.Private.CoreLib/shared/System/Collections/HashHelpers.cs delete mode 100644 src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs delete mode 100644 src/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index 90a656f52..5b7629f24 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -53,6 +53,7 @@ + @@ -67,7 +68,9 @@ + + diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs new file mode 100644 index 000000000..5b576973a --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs @@ -0,0 +1,1463 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; + +namespace System.Collections.Generic +{ + /// + /// Used internally to control behavior of insertion into a . + /// + internal enum InsertionBehavior : byte + { + /// + /// The default insertion behavior. + /// + None = 0, + + /// + /// Specifies that an existing entry with the same key should be overwritten if encountered. + /// + OverwriteExisting = 1, + + /// + /// Specifies that if an existing entry with the same key is encountered, an exception should be thrown. + /// + ThrowOnExisting = 2 + } + + [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] + [DebuggerDisplay("Count = {Count}")] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public class Dictionary : IDictionary, IDictionary, IReadOnlyDictionary, ISerializable, IDeserializationCallback + { + private struct Entry + { + public int hashCode; // Lower 31 bits of hash code, -1 if unused + public int next; // Index of next entry, -1 if last + public TKey key; // Key of entry + public TValue value; // Value of entry + } + + private int[] buckets; + private Entry[] entries; + private int count; + private int version; + private int freeList; + private int freeCount; + private IEqualityComparer comparer; + private KeyCollection keys; + private ValueCollection values; + private object _syncRoot; + + // constants for serialization + private const string VersionName = "Version"; // Do not rename (binary serialization) + private const string HashSizeName = "HashSize"; // Do not rename (binary serialization). Must save buckets.Length + private const string KeyValuePairsName = "KeyValuePairs"; // Do not rename (binary serialization) + private const string ComparerName = "Comparer"; // Do not rename (binary serialization) + + public Dictionary() : this(0, null) { } + + public Dictionary(int capacity) : this(capacity, null) { } + + public Dictionary(IEqualityComparer comparer) : this(0, comparer) { } + + public Dictionary(int capacity, IEqualityComparer comparer) + { + if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity); + if (capacity > 0) Initialize(capacity); + this.comparer = comparer ?? EqualityComparer.Default; + + if (this.comparer == EqualityComparer.Default) + { + this.comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.Default; + } + } + + public Dictionary(IDictionary dictionary) : this(dictionary, null) { } + + public Dictionary(IDictionary dictionary, IEqualityComparer comparer) : + this(dictionary != null ? dictionary.Count : 0, comparer) + { + if (dictionary == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary); + } + + // It is likely that the passed-in dictionary is Dictionary. When this is the case, + // avoid the enumerator allocation and overhead by looping through the entries array directly. + // We only do this when dictionary is Dictionary and not a subclass, to maintain + // back-compat with subclasses that may have overridden the enumerator behavior. + if (dictionary.GetType() == typeof(Dictionary)) + { + Dictionary d = (Dictionary)dictionary; + int count = d.count; + Entry[] entries = d.entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + Add(entries[i].key, entries[i].value); + } + } + return; + } + + foreach (KeyValuePair pair in dictionary) + { + Add(pair.Key, pair.Value); + } + } + + public Dictionary(IEnumerable> collection) : this(collection, null) { } + + public Dictionary(IEnumerable> collection, IEqualityComparer comparer) : + this((collection as ICollection>)?.Count ?? 0, comparer) + { + if (collection == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); + } + + foreach (KeyValuePair pair in collection) + { + Add(pair.Key, pair.Value); + } + } + + protected Dictionary(SerializationInfo info, StreamingContext context) + { + // We can't do anything with the keys and values until the entire graph has been deserialized + // and we have a resonable estimate that GetHashCode is not going to fail. For the time being, + // we'll just cache this. The graph is not valid until OnDeserialization has been called. + HashHelpers.SerializationInfoTable.Add(this, info); + } + + public IEqualityComparer Comparer + { + get + { + return comparer; + } + } + + public int Count + { + get { return count - freeCount; } + } + + public KeyCollection Keys + { + get + { + if (keys == null) keys = new KeyCollection(this); + return keys; + } + } + + ICollection IDictionary.Keys + { + get + { + if (keys == null) keys = new KeyCollection(this); + return keys; + } + } + + IEnumerable IReadOnlyDictionary.Keys + { + get + { + if (keys == null) keys = new KeyCollection(this); + return keys; + } + } + + public ValueCollection Values + { + get + { + if (values == null) values = new ValueCollection(this); + return values; + } + } + + ICollection IDictionary.Values + { + get + { + if (values == null) values = new ValueCollection(this); + return values; + } + } + + IEnumerable IReadOnlyDictionary.Values + { + get + { + if (values == null) values = new ValueCollection(this); + return values; + } + } + + public TValue this[TKey key] + { + get + { + int i = FindEntry(key); + if (i >= 0) return entries[i].value; + ThrowHelper.ThrowKeyNotFoundException(); + return default(TValue); + } + set + { + bool modified = TryInsert(key, value, InsertionBehavior.OverwriteExisting); + Debug.Assert(modified); + } + } + + public void Add(TKey key, TValue value) + { + bool modified = TryInsert(key, value, InsertionBehavior.ThrowOnExisting); + Debug.Assert(modified); // If there was an existing key and the Add failed, an exception will already have been thrown. + } + + void ICollection>.Add(KeyValuePair keyValuePair) + { + Add(keyValuePair.Key, keyValuePair.Value); + } + + bool ICollection>.Contains(KeyValuePair keyValuePair) + { + int i = FindEntry(keyValuePair.Key); + if (i >= 0 && EqualityComparer.Default.Equals(entries[i].value, keyValuePair.Value)) + { + return true; + } + return false; + } + + bool ICollection>.Remove(KeyValuePair keyValuePair) + { + int i = FindEntry(keyValuePair.Key); + if (i >= 0 && EqualityComparer.Default.Equals(entries[i].value, keyValuePair.Value)) + { + Remove(keyValuePair.Key); + return true; + } + return false; + } + + public void Clear() + { + if (count > 0) + { + for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; + Array.Clear(entries, 0, count); + freeList = -1; + count = 0; + freeCount = 0; + version++; + } + } + + public bool ContainsKey(TKey key) + { + return FindEntry(key) >= 0; + } + + public bool ContainsValue(TValue value) + { + if (value == null) + { + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0 && entries[i].value == null) return true; + } + } + else + { + EqualityComparer c = EqualityComparer.Default; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true; + } + } + return false; + } + + private void CopyTo(KeyValuePair[] array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + int count = this.count; + Entry[] entries = this.entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + array[index++] = new KeyValuePair(entries[i].key, entries[i].value); + } + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info); + } + + info.AddValue(VersionName, version); + info.AddValue(ComparerName, comparer, typeof(IEqualityComparer)); + info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); // This is the length of the bucket array + + if (buckets != null) + { + var array = new KeyValuePair[Count]; + CopyTo(array, 0); + info.AddValue(KeyValuePairsName, array, typeof(KeyValuePair[])); + } + } + + private int FindEntry(TKey key) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + if (buckets != null) + { + int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; + for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) + { + if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i; + } + } + return -1; + } + + private void Initialize(int capacity) + { + int size = HashHelpers.GetPrime(capacity); + buckets = new int[size]; + for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; + entries = new Entry[size]; + freeList = -1; + } + + private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + if (buckets == null) Initialize(0); + int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; + int targetBucket = hashCode % buckets.Length; + int collisionCount = 0; + + for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) + { + if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) + { + if (behavior == InsertionBehavior.OverwriteExisting) + { + entries[i].value = value; + version++; + return true; + } + + if (behavior == InsertionBehavior.ThrowOnExisting) + { + ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key); + } + + return false; + } + collisionCount++; + } + + int index; + if (freeCount > 0) + { + index = freeList; + freeList = entries[index].next; + freeCount--; + } + else + { + if (count == entries.Length) + { + Resize(); + targetBucket = hashCode % buckets.Length; + } + index = count; + count++; + } + + entries[index].hashCode = hashCode; + entries[index].next = buckets[targetBucket]; + entries[index].key = key; + entries[index].value = value; + buckets[targetBucket] = index; + version++; + + // If we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing + // i.e. EqualityComparer.Default. + + if (collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer) + { + comparer = (IEqualityComparer)EqualityComparer.Default; + Resize(entries.Length, true); + } + + return true; + } + + public virtual void OnDeserialization(object sender) + { + SerializationInfo siInfo; + HashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo); + + if (siInfo == null) + { + // We can return immediately if this function is called twice. + // Note we remove the serialization info from the table at the end of this method. + return; + } + + int realVersion = siInfo.GetInt32(VersionName); + int hashsize = siInfo.GetInt32(HashSizeName); + comparer = (IEqualityComparer)siInfo.GetValue(ComparerName, typeof(IEqualityComparer)); + + if (hashsize != 0) + { + buckets = new int[hashsize]; + for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; + entries = new Entry[hashsize]; + freeList = -1; + + KeyValuePair[] array = (KeyValuePair[]) + siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair[])); + + if (array == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_MissingKeys); + } + + for (int i = 0; i < array.Length; i++) + { + if (array[i].Key == null) + { + ThrowHelper.ThrowSerializationException(ExceptionResource.Serialization_NullKey); + } + Add(array[i].Key, array[i].Value); + } + } + else + { + buckets = null; + } + + version = realVersion; + HashHelpers.SerializationInfoTable.Remove(this); + } + + private void Resize() + { + Resize(HashHelpers.ExpandPrime(count), false); + } + + private void Resize(int newSize, bool forceNewHashCodes) + { + Debug.Assert(newSize >= entries.Length); + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1; + Entry[] newEntries = new Entry[newSize]; + Array.Copy(entries, 0, newEntries, 0, count); + + if (forceNewHashCodes) + { + for (int i = 0; i < count; i++) + { + if (newEntries[i].hashCode != -1) + { + newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF); + } + } + } + + for (int i = 0; i < count; i++) + { + if (newEntries[i].hashCode >= 0) + { + int bucket = newEntries[i].hashCode % newSize; + newEntries[i].next = newBuckets[bucket]; + newBuckets[bucket] = i; + } + } + + buckets = newBuckets; + entries = newEntries; + } + + // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional + // statement to copy the value for entry being removed into the output parameter. + // Code has been intentionally duplicated for performance reasons. + public bool Remove(TKey key) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + if (buckets != null) + { + int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; + int bucket = hashCode % buckets.Length; + int last = -1; + int i = buckets[bucket]; + while (i >= 0) + { + ref Entry entry = ref entries[i]; + + if (entry.hashCode == hashCode && comparer.Equals(entry.key, key)) + { + if (last < 0) + { + buckets[bucket] = entry.next; + } + else + { + entries[last].next = entry.next; + } + entry.hashCode = -1; + entry.next = freeList; + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.key = default(TKey); + } + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.value = default(TValue); + } + freeList = i; + freeCount++; + version++; + return true; + } + + last = i; + i = entry.next; + } + } + return false; + } + + // This overload is a copy of the overload Remove(TKey key) with one additional + // statement to copy the value for entry being removed into the output parameter. + // Code has been intentionally duplicated for performance reasons. + public bool Remove(TKey key, out TValue value) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + + if (buckets != null) + { + int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; + int bucket = hashCode % buckets.Length; + int last = -1; + int i = buckets[bucket]; + while (i >= 0) + { + ref Entry entry = ref entries[i]; + + if (entry.hashCode == hashCode && comparer.Equals(entry.key, key)) + { + if (last < 0) + { + buckets[bucket] = entry.next; + } + else + { + entries[last].next = entry.next; + } + + value = entry.value; + + entry.hashCode = -1; + entry.next = freeList; + + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.key = default(TKey); + } + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + entry.value = default(TValue); + } + freeList = i; + freeCount++; + version++; + return true; + } + + last = i; + i = entry.next; + } + } + value = default(TValue); + return false; + } + + public bool TryGetValue(TKey key, out TValue value) + { + int i = FindEntry(key); + if (i >= 0) + { + value = entries[i].value; + return true; + } + value = default(TValue); + return false; + } + + public bool TryAdd(TKey key, TValue value) => TryInsert(key, value, InsertionBehavior.None); + + bool ICollection>.IsReadOnly + { + get { return false; } + } + + void ICollection>.CopyTo(KeyValuePair[] array, int index) + { + CopyTo(array, index); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + KeyValuePair[] pairs = array as KeyValuePair[]; + if (pairs != null) + { + CopyTo(pairs, index); + } + else if (array is DictionaryEntry[]) + { + DictionaryEntry[] dictEntryArray = array as DictionaryEntry[]; + Entry[] entries = this.entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value); + } + } + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + try + { + int count = this.count; + Entry[] entries = this.entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) + { + objects[index++] = new KeyValuePair(entries[i].key, entries[i].value); + } + } + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this, Enumerator.KeyValuePair); + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null); + } + return _syncRoot; + } + } + + bool IDictionary.IsFixedSize + { + get { return false; } + } + + bool IDictionary.IsReadOnly + { + get { return false; } + } + + ICollection IDictionary.Keys + { + get { return (ICollection)Keys; } + } + + ICollection IDictionary.Values + { + get { return (ICollection)Values; } + } + + object IDictionary.this[object key] + { + get + { + if (IsCompatibleKey(key)) + { + int i = FindEntry((TKey)key); + if (i >= 0) + { + return entries[i].value; + } + } + return null; + } + set + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); + + try + { + TKey tempKey = (TKey)key; + try + { + this[tempKey] = (TValue)value; + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue)); + } + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey)); + } + } + } + + private static bool IsCompatibleKey(object key) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + return (key is TKey); + } + + void IDictionary.Add(object key, object value) + { + if (key == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); + } + ThrowHelper.IfNullAndNullsAreIllegalThenThrow(value, ExceptionArgument.value); + + try + { + TKey tempKey = (TKey)key; + + try + { + Add(tempKey, (TValue)value); + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(TValue)); + } + } + catch (InvalidCastException) + { + ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey)); + } + } + + bool IDictionary.Contains(object key) + { + if (IsCompatibleKey(key)) + { + return ContainsKey((TKey)key); + } + + return false; + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + return new Enumerator(this, Enumerator.DictEntry); + } + + void IDictionary.Remove(object key) + { + if (IsCompatibleKey(key)) + { + Remove((TKey)key); + } + } + + public struct Enumerator : IEnumerator>, + IDictionaryEnumerator + { + private Dictionary dictionary; + private int version; + private int index; + private KeyValuePair current; + private int getEnumeratorRetType; // What should Enumerator.Current return? + + internal const int DictEntry = 1; + internal const int KeyValuePair = 2; + + internal Enumerator(Dictionary dictionary, int getEnumeratorRetType) + { + this.dictionary = dictionary; + version = dictionary.version; + index = 0; + this.getEnumeratorRetType = getEnumeratorRetType; + current = new KeyValuePair(); + } + + public bool MoveNext() + { + if (version != dictionary.version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. + // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue + while ((uint)index < (uint)dictionary.count) + { + ref Entry entry = ref dictionary.entries[index++]; + + if (entry.hashCode >= 0) + { + current = new KeyValuePair(entry.key, entry.value); + return true; + } + } + + index = dictionary.count + 1; + current = new KeyValuePair(); + return false; + } + + public KeyValuePair Current + { + get { return current; } + } + + public void Dispose() + { + } + + object IEnumerator.Current + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + if (getEnumeratorRetType == DictEntry) + { + return new System.Collections.DictionaryEntry(current.Key, current.Value); + } + else + { + return new KeyValuePair(current.Key, current.Value); + } + } + } + + void IEnumerator.Reset() + { + if (version != dictionary.version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + index = 0; + current = new KeyValuePair(); + } + + DictionaryEntry IDictionaryEnumerator.Entry + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + return new DictionaryEntry(current.Key, current.Value); + } + } + + object IDictionaryEnumerator.Key + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + return current.Key; + } + } + + object IDictionaryEnumerator.Value + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + return current.Value; + } + } + } + + [DebuggerTypeProxy(typeof(DictionaryKeyCollectionDebugView<,>))] + [DebuggerDisplay("Count = {Count}")] + public sealed class KeyCollection : ICollection, ICollection, IReadOnlyCollection + { + private Dictionary dictionary; + + public KeyCollection(Dictionary dictionary) + { + if (dictionary == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary); + } + this.dictionary = dictionary; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(dictionary); + } + + public void CopyTo(TKey[] array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < dictionary.Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) array[index++] = entries[i].key; + } + } + + public int Count + { + get { return dictionary.Count; } + } + + bool ICollection.IsReadOnly + { + get { return true; } + } + + void ICollection.Add(TKey item) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); + } + + void ICollection.Clear() + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); + } + + bool ICollection.Contains(TKey item) + { + return dictionary.ContainsKey(item); + } + + bool ICollection.Remove(TKey item) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet); + return false; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(dictionary); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(dictionary); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < dictionary.Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + TKey[] keys = array as TKey[]; + if (keys != null) + { + CopyTo(keys, index); + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + try + { + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) objects[index++] = entries[i].key; + } + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get { return ((ICollection)dictionary).SyncRoot; } + } + + public struct Enumerator : IEnumerator, System.Collections.IEnumerator + { + private Dictionary dictionary; + private int index; + private int version; + private TKey currentKey; + + internal Enumerator(Dictionary dictionary) + { + this.dictionary = dictionary; + version = dictionary.version; + index = 0; + currentKey = default(TKey); + } + + public void Dispose() + { + } + + public bool MoveNext() + { + if (version != dictionary.version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + while ((uint)index < (uint)dictionary.count) + { + ref Entry entry = ref dictionary.entries[index++]; + + if (entry.hashCode >= 0) + { + currentKey = entry.key; + return true; + } + } + + index = dictionary.count + 1; + currentKey = default(TKey); + return false; + } + + public TKey Current + { + get + { + return currentKey; + } + } + + object System.Collections.IEnumerator.Current + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + return currentKey; + } + } + + void System.Collections.IEnumerator.Reset() + { + if (version != dictionary.version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + index = 0; + currentKey = default(TKey); + } + } + } + + [DebuggerTypeProxy(typeof(DictionaryValueCollectionDebugView<,>))] + [DebuggerDisplay("Count = {Count}")] + public sealed class ValueCollection : ICollection, ICollection, IReadOnlyCollection + { + private Dictionary dictionary; + + public ValueCollection(Dictionary dictionary) + { + if (dictionary == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary); + } + this.dictionary = dictionary; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(dictionary); + } + + public void CopyTo(TValue[] array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < dictionary.Count) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) array[index++] = entries[i].value; + } + } + + public int Count + { + get { return dictionary.Count; } + } + + bool ICollection.IsReadOnly + { + get { return true; } + } + + void ICollection.Add(TValue item) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); + } + + bool ICollection.Remove(TValue item) + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); + return false; + } + + void ICollection.Clear() + { + ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); + } + + bool ICollection.Contains(TValue item) + { + return dictionary.ContainsValue(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(dictionary); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(dictionary); + } + + void ICollection.CopyTo(Array array, int index) + { + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + } + + if (array.Rank != 1) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + + if (array.GetLowerBound(0) != 0) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); + } + + if (index < 0 || index > array.Length) + { + ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); + } + + if (array.Length - index < dictionary.Count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); + + TValue[] values = array as TValue[]; + if (values != null) + { + CopyTo(values, index); + } + else + { + object[] objects = array as object[]; + if (objects == null) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + + int count = dictionary.count; + Entry[] entries = dictionary.entries; + try + { + for (int i = 0; i < count; i++) + { + if (entries[i].hashCode >= 0) objects[index++] = entries[i].value; + } + } + catch (ArrayTypeMismatchException) + { + ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); + } + } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get { return ((ICollection)dictionary).SyncRoot; } + } + + public struct Enumerator : IEnumerator, System.Collections.IEnumerator + { + private Dictionary dictionary; + private int index; + private int version; + private TValue currentValue; + + internal Enumerator(Dictionary dictionary) + { + this.dictionary = dictionary; + version = dictionary.version; + index = 0; + currentValue = default(TValue); + } + + public void Dispose() + { + } + + public bool MoveNext() + { + if (version != dictionary.version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + while ((uint)index < (uint)dictionary.count) + { + ref Entry entry = ref dictionary.entries[index++]; + + if (entry.hashCode >= 0) + { + currentValue = entry.value; + return true; + } + } + index = dictionary.count + 1; + currentValue = default(TValue); + return false; + } + + public TValue Current + { + get + { + return currentValue; + } + } + + object System.Collections.IEnumerator.Current + { + get + { + if (index == 0 || (index == dictionary.count + 1)) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen(); + } + + return currentValue; + } + } + + void System.Collections.IEnumerator.Reset() + { + if (version != dictionary.version) + { + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + index = 0; + currentValue = default(TValue); + } + } + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs new file mode 100644 index 000000000..ef44fefc8 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Serialization; + +namespace System.Collections.Generic +{ + // NonRandomizedStringEqualityComparer is the comparer used by default with the Dictionary + // We use NonRandomizedStringEqualityComparer as default comparer as it doesnt use the randomized string hashing which + // keeps the performance not affected till we hit collision threshold and then we switch to the comparer which is using + // randomized string hashing. + [Serializable] // Required for compatibility with .NET Core 2.0 as we exposed the NonRandomizedStringEqualityComparer inside the serialization blob +#if CORECLR + internal +#else + public +#endif + sealed class NonRandomizedStringEqualityComparer : EqualityComparer, ISerializable + { + internal static new IEqualityComparer Default { get; } = new NonRandomizedStringEqualityComparer(); + + private NonRandomizedStringEqualityComparer() { } + + // This is used by the serialization engine. + private NonRandomizedStringEqualityComparer(SerializationInfo information, StreamingContext context) { } + + public sealed override bool Equals(string x, string y) => string.Equals(x, y); + + public sealed override int GetHashCode(string obj) => obj?.GetLegacyNonRandomizedHashCode() ?? 0; + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + // We are doing this to stay compatible with .NET Framework. + info.SetType(typeof(GenericEqualityComparer)); + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/Collections/HashHelpers.cs b/src/System.Private.CoreLib/shared/System/Collections/HashHelpers.cs new file mode 100644 index 000000000..49cff85b5 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Collections/HashHelpers.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Threading; + +namespace System.Collections +{ + internal static class HashHelpers + { + public const int HashCollisionThreshold = 100; + + public const int HashPrime = 101; + + // Table of prime numbers to use as hash table sizes. + // A typical resize algorithm would pick the smallest prime number in this array + // that is larger than twice the previous capacity. + // Suppose our Hashtable currently has capacity x and enough elements are added + // such that a resize needs to occur. Resizing first computes 2x then finds the + // first prime in the table greater than 2x, i.e. if primes are ordered + // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n. + // Doubling is important for preserving the asymptotic complexity of the + // hashtable operations such as add. Having a prime guarantees that double + // hashing does not lead to infinite loops. IE, your hash function will be + // h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime. + public static readonly int[] primes = { + 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, + 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, + 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369}; + + public static bool IsPrime(int candidate) + { + if ((candidate & 1) != 0) + { + int limit = (int)Math.Sqrt(candidate); + for (int divisor = 3; divisor <= limit; divisor += 2) + { + if ((candidate % divisor) == 0) + return false; + } + return true; + } + return (candidate == 2); + } + + public static int GetPrime(int min) + { + if (min < 0) + throw new ArgumentException(SR.Arg_HTCapacityOverflow); + + for (int i = 0; i < primes.Length; i++) + { + int prime = primes[i]; + if (prime >= min) return prime; + } + + //outside of our predefined table. + //compute the hard way. + for (int i = (min | 1); i < Int32.MaxValue; i += 2) + { + if (IsPrime(i) && ((i - 1) % HashPrime != 0)) + return i; + } + return min; + } + + // Returns size of hashtable to grow to. + public static int ExpandPrime(int oldSize) + { + int newSize = 2 * oldSize; + + // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) + { + Debug.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength"); + return MaxPrimeArrayLength; + } + + return GetPrime(newSize); + } + + + // This is the maximum prime smaller than Array.MaxArrayLength + public const int MaxPrimeArrayLength = 0x7FEFFFFD; + + + // Used by Hashtable and Dictionary's SeralizationInfo .ctor's to store the SeralizationInfo + // object until OnDeserialization is called. + private static ConditionalWeakTable s_serializationInfoTable; + + internal static ConditionalWeakTable SerializationInfoTable + { + get + { + if (s_serializationInfoTable == null) + Interlocked.CompareExchange(ref s_serializationInfoTable, new ConditionalWeakTable(), null); + + return s_serializationInfoTable; + } + } + } +} diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 941ff5475..3c4f91f71 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -195,8 +195,6 @@ - - @@ -709,9 +707,6 @@ System\Collections\Generic\LowLevelDictionary.cs - - System\Collections\HashHelpers.cs - System\Collections\Concurrent\ConcurrentUnifier.cs diff --git a/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs deleted file mode 100644 index bf5e4df08..000000000 --- a/src/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ /dev/null @@ -1,1447 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Internal.Runtime.CompilerServices; -using System; -using System.Collections; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.Serialization; - -namespace System.Collections.Generic -{ - /// - /// Used internally to control behavior of insertion into a . - /// - internal enum InsertionBehavior : byte - { - /// - /// The default insertion behavior. - /// - None = 0, - - /// - /// Specifies that an existing entry with the same key should be overwritten if encountered. - /// - OverwriteExisting = 1, - - /// - /// Specifies that if an existing entry with the same key is encountered, an exception should be thrown. - /// - ThrowOnExisting = 2 - } - - [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] - [DebuggerDisplay("Count = {Count}")] - [Serializable] - [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public class Dictionary : IDictionary, IDictionary, IReadOnlyDictionary, ISerializable, IDeserializationCallback - { - private struct Entry - { - public int hashCode; // Lower 31 bits of hash code, -1 if unused - public int next; // Index of next entry, -1 if last - public TKey key; // Key of entry - public TValue value; // Value of entry - } - - private int[] buckets; - private Entry[] entries; - private int count; - private int version; - - private int freeList; - private int freeCount; - private IEqualityComparer comparer; - private KeyCollection keys; - private ValueCollection values; - private object _syncRoot; - - // constants for serialization - private const string VersionName = "Version"; // Do not rename (binary serialization) - private const string HashSizeName = "HashSize"; // Do not rename (binary serialization). Must save buckets.Length - private const string KeyValuePairsName = "KeyValuePairs"; // Do not rename (binary serialization) - private const string ComparerName = "Comparer"; // Do not rename (binary serialization) - - public Dictionary() : this(0, null) { } - - public Dictionary(int capacity) : this(capacity, null) { } - - public Dictionary(IEqualityComparer comparer) : this(0, comparer) { } - - public Dictionary(int capacity, IEqualityComparer comparer) - { - if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), capacity, SR.ArgumentOutOfRange_NeedNonNegNum); - if (capacity > 0) Initialize(capacity); - this.comparer = comparer ?? EqualityComparer.Default; - - if (this.comparer == EqualityComparer.Default) - { - this.comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.Default; - } - } - - public Dictionary(IDictionary dictionary) : this(dictionary, null) { } - - public Dictionary(IDictionary dictionary, IEqualityComparer comparer) : - this(dictionary != null ? dictionary.Count : 0, comparer) - { - if (dictionary == null) - { - throw new ArgumentNullException(nameof(dictionary)); - } - - // It is likely that the passed-in dictionary is Dictionary. When this is the case, - // avoid the enumerator allocation and overhead by looping through the entries array directly. - // We only do this when dictionary is Dictionary and not a subclass, to maintain - // back-compat with subclasses that may have overridden the enumerator behavior. - if (dictionary.GetType() == typeof(Dictionary)) - { - Dictionary d = (Dictionary)dictionary; - int count = d.count; - Entry[] entries = d.entries; - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0) - { - Add(entries[i].key, entries[i].value); - } - } - return; - } - - foreach (KeyValuePair pair in dictionary) - { - Add(pair.Key, pair.Value); - } - } - - public Dictionary(IEnumerable> collection) : this(collection, null) { } - - public Dictionary(IEnumerable> collection, IEqualityComparer comparer) : - this((collection as ICollection>)?.Count ?? 0, comparer) - { - if (collection == null) - { - throw new ArgumentNullException(nameof(collection)); - } - - foreach (KeyValuePair pair in collection) - { - Add(pair.Key, pair.Value); - } - } - - protected Dictionary(SerializationInfo info, StreamingContext context) - { - // We can't do anything with the keys and values until the entire graph has been deserialized - // and we have a resonable estimate that GetHashCode is not going to fail. For the time being, - // we'll just cache this. The graph is not valid until OnDeserialization has been called. - DictionaryHashHelpers.SerializationInfoTable.Add(this, info); - } - - public IEqualityComparer Comparer - { - get - { - return comparer; - } - } - - public int Count - { - get { return count - freeCount; } - } - - public KeyCollection Keys - { - get - { - if (keys == null) keys = new KeyCollection(this); - return keys; - } - } - - ICollection IDictionary.Keys - { - get - { - if (keys == null) keys = new KeyCollection(this); - return keys; - } - } - - IEnumerable IReadOnlyDictionary.Keys - { - get - { - if (keys == null) keys = new KeyCollection(this); - return keys; - } - } - - public ValueCollection Values - { - get - { - if (values == null) values = new ValueCollection(this); - return values; - } - } - - ICollection IDictionary.Values - { - get - { - if (values == null) values = new ValueCollection(this); - return values; - } - } - - IEnumerable IReadOnlyDictionary.Values - { - get - { - if (values == null) values = new ValueCollection(this); - return values; - } - } - - public TValue this[TKey key] - { - get - { - int i = FindEntry(key); - if (i >= 0) return entries[i].value; - throw new KeyNotFoundException(); - } - set - { - bool modified = TryInsert(key, value, InsertionBehavior.OverwriteExisting); - Debug.Assert(modified); - } - } - - public void Add(TKey key, TValue value) - { - bool modified = TryInsert(key, value, InsertionBehavior.ThrowOnExisting); - Debug.Assert(modified); // If there was an existing key and the Add failed, an exception will already have been thrown. - } - - void ICollection>.Add(KeyValuePair keyValuePair) - { - Add(keyValuePair.Key, keyValuePair.Value); - } - - bool ICollection>.Contains(KeyValuePair keyValuePair) - { - int i = FindEntry(keyValuePair.Key); - if (i >= 0 && EqualityComparer.Default.Equals(entries[i].value, keyValuePair.Value)) - { - return true; - } - return false; - } - - bool ICollection>.Remove(KeyValuePair keyValuePair) - { - int i = FindEntry(keyValuePair.Key); - if (i >= 0 && EqualityComparer.Default.Equals(entries[i].value, keyValuePair.Value)) - { - Remove(keyValuePair.Key); - return true; - } - return false; - } - - public void Clear() - { - if (count > 0) - { - for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; - Array.Clear(entries, 0, count); - freeList = -1; - count = 0; - freeCount = 0; - version++; - } - } - - public bool ContainsKey(TKey key) - { - return FindEntry(key) >= 0; - } - - public bool ContainsValue(TValue value) - { - if (value == null) - { - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0 && entries[i].value == null) return true; - } - } - else - { - EqualityComparer c = EqualityComparer.Default; - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true; - } - } - return false; - } - - private void CopyTo(KeyValuePair[] array, int index) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - - if (index < 0 || index > array.Length) - { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); - } - - if (array.Length - index < Count) - { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); - } - - int count = this.count; - Entry[] entries = this.entries; - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0) - { - array[index++] = new KeyValuePair(entries[i].key, entries[i].value); - } - } - } - - public Enumerator GetEnumerator() - { - return new Enumerator(this, Enumerator.KeyValuePair); - } - - IEnumerator> IEnumerable>.GetEnumerator() - { - return new Enumerator(this, Enumerator.KeyValuePair); - } - - public virtual void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } - - info.AddValue(VersionName, version); - info.AddValue(ComparerName, comparer, typeof(IEqualityComparer)); - info.AddValue(HashSizeName, buckets == null ? 0 : buckets.Length); // This is the length of the bucket array - - if (buckets != null) - { - var array = new KeyValuePair[Count]; - CopyTo(array, 0); - info.AddValue(KeyValuePairsName, array, typeof(KeyValuePair[])); - } - } - - private int FindEntry(TKey key) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (buckets != null) - { - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) - { - if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i; - } - } - return -1; - } - - private void Initialize(int capacity) - { - int size = HashHelpers.GetPrime(capacity); - buckets = new int[size]; - for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; - entries = new Entry[size]; - freeList = -1; - } - - private bool TryInsert(TKey key, TValue value, InsertionBehavior behavior) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (buckets == null) Initialize(0); - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - int targetBucket = hashCode % buckets.Length; - int collisionCount = 0; - - for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) - { - if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) - { - if (behavior == InsertionBehavior.OverwriteExisting) - { - entries[i].value = value; - version++; - return true; - } - - if (behavior == InsertionBehavior.ThrowOnExisting) - { - throw new ArgumentException(SR.Format(SR.Argument_AddingDuplicate, key)); - } - - return false; - } - collisionCount++; - } - - int index; - - if (freeCount > 0) - { - index = freeList; - freeList = entries[index].next; - freeCount--; - } - else - { - if (count == entries.Length) - { - Resize(); - targetBucket = hashCode % buckets.Length; - } - index = count; - count++; - } - - entries[index].hashCode = hashCode; - entries[index].next = buckets[targetBucket]; - entries[index].key = key; - entries[index].value = value; - buckets[targetBucket] = index; - version++; - - if (collisionCount > HashHelpers.HashCollisionThreshold && comparer is NonRandomizedStringEqualityComparer) - { - comparer = (IEqualityComparer)EqualityComparer.Default; - Resize(entries.Length, true); - } - - return true; - } - - public virtual void OnDeserialization(object sender) - { - SerializationInfo siInfo; - DictionaryHashHelpers.SerializationInfoTable.TryGetValue(this, out siInfo); - if (siInfo == null) - { - // We can return immediately if this function is called twice. - // Note we remove the serialization info from the table at the end of this method. - return; - } - - int realVersion = siInfo.GetInt32(VersionName); - int hashsize = siInfo.GetInt32(HashSizeName); - comparer = (IEqualityComparer)siInfo.GetValue(ComparerName, typeof(IEqualityComparer)); - - if (hashsize != 0) - { - buckets = new int[hashsize]; - for (int i = 0; i < buckets.Length; i++) buckets[i] = -1; - entries = new Entry[hashsize]; - freeList = -1; - - KeyValuePair[] array = - (KeyValuePair[])siInfo.GetValue(KeyValuePairsName, typeof(KeyValuePair[])); - - if (array == null) - { - throw new SerializationException(SR.Serialization_MissingKeys); - } - - for (int i = 0; i < array.Length; i++) - { - if (array[i].Key == null) - { - throw new SerializationException(SR.Serialization_NullKey); - } - Add(array[i].Key, array[i].Value); - } - } - else - { - buckets = null; - } - - version = realVersion; - DictionaryHashHelpers.SerializationInfoTable.Remove(this); - } - - private void Resize() - { - Resize(HashHelpers.ExpandPrime(count), false); - } - - private void Resize(int newSize, bool forceNewHashCodes) - { - Debug.Assert(newSize >= entries.Length); - int[] newBuckets = new int[newSize]; - for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1; - - Entry[] newEntries = new Entry[newSize]; - Array.Copy(entries, 0, newEntries, 0, count); - - if (forceNewHashCodes) - { - for (int i = 0; i < count; i++) - { - if (newEntries[i].hashCode != -1) - { - newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF); - } - } - } - - for (int i = 0; i < count; i++) - { - if (newEntries[i].hashCode >= 0) - { - int bucket = newEntries[i].hashCode % newSize; - newEntries[i].next = newBuckets[bucket]; - newBuckets[bucket] = i; - } - } - - buckets = newBuckets; - entries = newEntries; - } - - // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional - // statement to copy the value for entry being removed into the output parameter. - // Code has been intentionally duplicated for performance reasons. - public bool Remove(TKey key) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (buckets != null) - { - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - int bucket = hashCode % buckets.Length; - int last = -1; - for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next) - { - if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) - { - if (last < 0) - { - buckets[bucket] = entries[i].next; - } - else - { - entries[last].next = entries[i].next; - } - entries[i].hashCode = -1; - entries[i].next = freeList; - entries[i].key = default(TKey); - entries[i].value = default(TValue); - freeList = i; - freeCount++; - version++; - return true; - } - } - } - return false; - } - - // This overload is a copy of the overload Remove(TKey key) with one additional - // statement to copy the value for entry being removed into the output parameter. - // Code has been intentionally duplicated for performance reasons. - public bool Remove(TKey key, out TValue value) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (buckets != null) - { - int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; - int bucket = hashCode % buckets.Length; - int last = -1; - for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next) - { - if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) - { - if (last < 0) - { - buckets[bucket] = entries[i].next; - } - else - { - entries[last].next = entries[i].next; - } - - value = entries[i].value; - - entries[i].hashCode = -1; - entries[i].next = freeList; - entries[i].key = default(TKey); - entries[i].value = default(TValue); - freeList = i; - freeCount++; - version++; - return true; - } - } - } - value = default(TValue); - return false; - } - - public bool TryGetValue(TKey key, out TValue value) - { - int i = FindEntry(key); - if (i >= 0) - { - value = entries[i].value; - return true; - } - value = default(TValue); - return false; - } - - public bool TryAdd(TKey key, TValue value) => TryInsert(key, value, InsertionBehavior.None); - - bool ICollection>.IsReadOnly - { - get { return false; } - } - - void ICollection>.CopyTo(KeyValuePair[] array, int index) - { - CopyTo(array, index); - } - - void ICollection.CopyTo(Array array, int index) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - - if (array.Rank != 1) - { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); - } - - if (array.GetLowerBound(0) != 0) - { - throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); - } - - if (index < 0 || index > array.Length) - { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); - } - - if (array.Length - index < Count) - { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); - } - - KeyValuePair[] pairs = array as KeyValuePair[]; - if (pairs != null) - { - CopyTo(pairs, index); - } - else if (array is DictionaryEntry[]) - { - DictionaryEntry[] dictEntryArray = array as DictionaryEntry[]; - Entry[] entries = this.entries; - - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0) - { - dictEntryArray[index++] = new DictionaryEntry(entries[i].key, entries[i].value); - } - } - } - else - { - object[] objects = array as object[]; - if (objects == null) - { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); - } - - try - { - int count = this.count; - Entry[] entries = this.entries; - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0) - { - objects[index++] = new KeyValuePair(entries[i].key, entries[i].value); - } - } - } - catch (ArrayTypeMismatchException) - { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); - } - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(this, Enumerator.KeyValuePair); - } - - bool ICollection.IsSynchronized - { - get { return false; } - } - - object ICollection.SyncRoot - { - get - { - if (_syncRoot == null) - { - System.Threading.Interlocked.CompareExchange(ref _syncRoot, new object(), null); - } - return _syncRoot; - } - } - - bool IDictionary.IsFixedSize - { - get { return false; } - } - - bool IDictionary.IsReadOnly - { - get { return false; } - } - - ICollection IDictionary.Keys - { - get { return (ICollection)Keys; } - } - - ICollection IDictionary.Values - { - get { return (ICollection)Values; } - } - - object IDictionary.this[object key] - { - get - { - if (IsCompatibleKey(key)) - { - int i = FindEntry((TKey)key); - if (i >= 0) - { - return entries[i].value; - } - } - return null; - } - set - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - if (value == null && !(default(TValue) == null)) - throw new ArgumentNullException(nameof(value)); - - try - { - TKey tempKey = (TKey)key; - try - { - this[tempKey] = (TValue)value; - } - catch (InvalidCastException) - { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(TValue)), nameof(value)); - } - } - catch (InvalidCastException) - { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, key, typeof(TKey)), nameof(key)); - } - } - } - - private static bool IsCompatibleKey(object key) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - return (key is TKey); - } - - void IDictionary.Add(object key, object value) - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (value == null && !(default(TValue) == null)) - throw new ArgumentNullException(nameof(value)); - - try - { - TKey tempKey = (TKey)key; - - try - { - Add(tempKey, (TValue)value); - } - catch (InvalidCastException) - { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, value, typeof(TValue)), nameof(value)); - } - } - catch (InvalidCastException) - { - throw new ArgumentException(SR.Format(SR.Arg_WrongType, key, typeof(TKey)), nameof(key)); - } - } - - bool IDictionary.Contains(object key) - { - if (IsCompatibleKey(key)) - { - return ContainsKey((TKey)key); - } - - return false; - } - - IDictionaryEnumerator IDictionary.GetEnumerator() - { - return new Enumerator(this, Enumerator.DictEntry); - } - - void IDictionary.Remove(object key) - { - if (IsCompatibleKey(key)) - { - Remove((TKey)key); - } - } - - public struct Enumerator : IEnumerator>, - IDictionaryEnumerator - { - private Dictionary dictionary; - private int version; - private int index; - private KeyValuePair current; - private int getEnumeratorRetType; // What should Enumerator.Current return? - - internal const int DictEntry = 1; - internal const int KeyValuePair = 2; - - internal Enumerator(Dictionary dictionary, int getEnumeratorRetType) - { - this.dictionary = dictionary; - version = dictionary.version; - index = 0; - this.getEnumeratorRetType = getEnumeratorRetType; - current = new KeyValuePair(); - } - - public bool MoveNext() - { - if (version != dictionary.version) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); - } - - // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends. - // dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue - while ((uint)index < (uint)dictionary.count) - { - if (dictionary.entries[index].hashCode >= 0) - { - current = new KeyValuePair(dictionary.entries[index].key, dictionary.entries[index].value); - index++; - return true; - } - index++; - } - - index = dictionary.count + 1; - current = new KeyValuePair(); - return false; - } - - public KeyValuePair Current - { - get { return current; } - } - - public void Dispose() - { - } - - object IEnumerator.Current - { - get - { - if (index == 0 || (index == dictionary.count + 1)) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); - } - - if (getEnumeratorRetType == DictEntry) - { - return new System.Collections.DictionaryEntry(current.Key, current.Value); - } - else - { - return new KeyValuePair(current.Key, current.Value); - } - } - } - - void IEnumerator.Reset() - { - if (version != dictionary.version) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); - } - - index = 0; - current = new KeyValuePair(); - } - - DictionaryEntry IDictionaryEnumerator.Entry - { - get - { - if (index == 0 || (index == dictionary.count + 1)) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); - } - - return new DictionaryEntry(current.Key, current.Value); - } - } - - object IDictionaryEnumerator.Key - { - get - { - if (index == 0 || (index == dictionary.count + 1)) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); - } - - return current.Key; - } - } - - object IDictionaryEnumerator.Value - { - get - { - if (index == 0 || (index == dictionary.count + 1)) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); - } - - return current.Value; - } - } - } - - [DebuggerTypeProxy(typeof(DictionaryKeyCollectionDebugView<,>))] - [DebuggerDisplay("Count = {Count}")] - public sealed class KeyCollection : ICollection, ICollection, IReadOnlyCollection - { - private Dictionary dictionary; - - public KeyCollection(Dictionary dictionary) - { - if (dictionary == null) - { - throw new ArgumentNullException(nameof(dictionary)); - } - this.dictionary = dictionary; - } - - public Enumerator GetEnumerator() - { - return new Enumerator(dictionary); - } - - public void CopyTo(TKey[] array, int index) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - - if (index < 0 || index > array.Length) - { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); - } - - if (array.Length - index < dictionary.Count) - { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); - } - - int count = dictionary.count; - Entry[] entries = dictionary.entries; - - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0) array[index++] = entries[i].key; - } - } - - public int Count - { - get { return dictionary.Count; } - } - - bool ICollection.IsReadOnly - { - get { return true; } - } - - void ICollection.Add(TKey item) - { - throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); - } - - void ICollection.Clear() - { - throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); - } - - bool ICollection.Contains(TKey item) - { - return dictionary.ContainsKey(item); - } - - bool ICollection.Remove(TKey item) - { - throw new NotSupportedException(SR.NotSupported_KeyCollectionSet); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(dictionary); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(dictionary); - } - - void ICollection.CopyTo(Array array, int index) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - - if (array.Rank != 1) - { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); - } - - if (array.GetLowerBound(0) != 0) - { - throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); - } - - if (index < 0 || index > array.Length) - { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); - } - - if (array.Length - index < dictionary.Count) - { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); - } - - TKey[] keys = array as TKey[]; - if (keys != null) - { - CopyTo(keys, index); - } - else - { - object[] objects = array as object[]; - if (objects == null) - { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); - } - - int count = dictionary.count; - Entry[] entries = dictionary.entries; - - try - { - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0) objects[index++] = entries[i].key; - } - } - catch (ArrayTypeMismatchException) - { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); - } - } - } - - bool ICollection.IsSynchronized - { - get { return false; } - } - - object ICollection.SyncRoot - { - get { return ((ICollection)dictionary).SyncRoot; } - } - - public struct Enumerator : IEnumerator, System.Collections.IEnumerator - { - private Dictionary dictionary; - private int index; - private int version; - private TKey currentKey; - - internal Enumerator(Dictionary dictionary) - { - this.dictionary = dictionary; - version = dictionary.version; - index = 0; - currentKey = default(TKey); - } - - public void Dispose() - { - } - - public bool MoveNext() - { - if (version != dictionary.version) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); - } - - while ((uint)index < (uint)dictionary.count) - { - if (dictionary.entries[index].hashCode >= 0) - { - currentKey = dictionary.entries[index].key; - index++; - return true; - } - index++; - } - - index = dictionary.count + 1; - currentKey = default(TKey); - return false; - } - - public TKey Current - { - get - { - return currentKey; - } - } - - object System.Collections.IEnumerator.Current - { - get - { - if (index == 0 || (index == dictionary.count + 1)) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); - } - - return currentKey; - } - } - - void System.Collections.IEnumerator.Reset() - { - if (version != dictionary.version) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); - } - - index = 0; - currentKey = default(TKey); - } - } - } - - [DebuggerTypeProxy(typeof(DictionaryValueCollectionDebugView<,>))] - [DebuggerDisplay("Count = {Count}")] - public sealed class ValueCollection : ICollection, ICollection, IReadOnlyCollection - { - private Dictionary dictionary; - - public ValueCollection(Dictionary dictionary) - { - if (dictionary == null) - { - throw new ArgumentNullException(nameof(dictionary)); - } - this.dictionary = dictionary; - } - - public Enumerator GetEnumerator() - { - return new Enumerator(dictionary); - } - - public void CopyTo(TValue[] array, int index) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - - if (index < 0 || index > array.Length) - { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); - } - - if (array.Length - index < dictionary.Count) - { - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); - } - - int count = dictionary.count; - Entry[] entries = dictionary.entries; - - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0) array[index++] = entries[i].value; - } - } - - public int Count - { - get { return dictionary.Count; } - } - - bool ICollection.IsReadOnly - { - get { return true; } - } - - void ICollection.Add(TValue item) - { - throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); - } - - bool ICollection.Remove(TValue item) - { - throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); - } - - void ICollection.Clear() - { - throw new NotSupportedException(SR.NotSupported_ValueCollectionSet); - } - - bool ICollection.Contains(TValue item) - { - return dictionary.ContainsValue(item); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(dictionary); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return new Enumerator(dictionary); - } - - void ICollection.CopyTo(Array array, int index) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - - if (array.Rank != 1) - { - throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); - } - - if (array.GetLowerBound(0) != 0) - { - throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); - } - - if (index < 0 || index > array.Length) - { - throw new ArgumentOutOfRangeException(nameof(index), index, SR.ArgumentOutOfRange_Index); - } - - if (array.Length - index < dictionary.Count) - throw new ArgumentException(SR.Arg_ArrayPlusOffTooSmall); - - TValue[] values = array as TValue[]; - if (values != null) - { - CopyTo(values, index); - } - else - { - object[] objects = array as object[]; - if (objects == null) - { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); - } - - int count = dictionary.count; - Entry[] entries = dictionary.entries; - - try - { - for (int i = 0; i < count; i++) - { - if (entries[i].hashCode >= 0) objects[index++] = entries[i].value; - } - } - catch (ArrayTypeMismatchException) - { - throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array)); - } - } - } - - bool ICollection.IsSynchronized - { - get { return false; } - } - - object ICollection.SyncRoot - { - get { return ((ICollection)dictionary).SyncRoot; } - } - - public struct Enumerator : IEnumerator, System.Collections.IEnumerator - { - private Dictionary dictionary; - private int index; - private int version; - private TValue currentValue; - - internal Enumerator(Dictionary dictionary) - { - this.dictionary = dictionary; - version = dictionary.version; - index = 0; - currentValue = default(TValue); - } - - public void Dispose() - { - } - - public bool MoveNext() - { - if (version != dictionary.version) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); - } - - while ((uint)index < (uint)dictionary.count) - { - if (dictionary.entries[index].hashCode >= 0) - { - currentValue = dictionary.entries[index].value; - index++; - return true; - } - index++; - } - index = dictionary.count + 1; - currentValue = default(TValue); - return false; - } - - public TValue Current - { - get - { - return currentValue; - } - } - - object System.Collections.IEnumerator.Current - { - get - { - if (index == 0 || (index == dictionary.count + 1)) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); - } - - return currentValue; - } - } - - void System.Collections.IEnumerator.Reset() - { - if (version != dictionary.version) - { - throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); - } - index = 0; - currentValue = default(TValue); - } - } - } - } - - internal class DictionaryHashHelpers - { - internal static ConditionalWeakTable SerializationInfoTable { get; } = new ConditionalWeakTable(); - } -} diff --git a/src/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/src/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs deleted file mode 100644 index a1f3e38bd..000000000 --- a/src/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Collections.Generic -{ - // NonRandomizedStringEqualityComparer is the comparer used by default with the Dictionary - // As the randomized string hashing is now turned on with no opt-out, we need to keep the performance not affected - // as much as possible in the main stream scenarios like Dictionary - // We use NonRandomizedStringEqualityComparer as default comparer as it doesnt use the randomized string hashing which - // keep the performance not affected till we hit collision threshold and then we switch to the comparer which is using - // randomized string hashing. - [Serializable] - [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public sealed class NonRandomizedStringEqualityComparer : EqualityComparer - { - private static volatile IEqualityComparer s_nonRandomizedComparer; - - internal static new IEqualityComparer Default => s_nonRandomizedComparer ?? (s_nonRandomizedComparer = new NonRandomizedStringEqualityComparer()); - - public sealed override bool Equals(string x, string y) => string.Equals(x, y); - - public sealed override int GetHashCode(string obj) - { - if (obj == null) - return 0; - return obj.GetLegacyNonRandomizedHashCode(); - } - } -} diff --git a/src/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/System.Private.CoreLib/src/System/ThrowHelper.cs index 4ca4103dc..f0c089cb3 100644 --- a/src/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -35,8 +35,10 @@ // multiple times for different instantiation. // +using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.Serialization; namespace System { @@ -102,6 +104,38 @@ namespace System ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); } + private static ArgumentException GetWrongKeyTypeArgumentException(object key, Type targetType) + { + return new ArgumentException(SR.Format(SR.Arg_WrongType, key, targetType), nameof(key)); + } + internal static void ThrowWrongKeyTypeArgumentException(object key, Type targetType) + { + throw GetWrongKeyTypeArgumentException(key, targetType); + } + + private static ArgumentException GetWrongValueTypeArgumentException(object value, Type targetType) + { + return new ArgumentException(SR.Format(SR.Arg_WrongType, value, targetType), nameof(value)); + } + internal static void ThrowWrongValueTypeArgumentException(object value, Type targetType) + { + throw GetWrongValueTypeArgumentException(value, targetType); + } + + private static ArgumentException GetAddingDuplicateWithKeyArgumentException(object key) + { + return new ArgumentException(SR.Format(SR.Argument_AddingDuplicate, key)); + } + internal static void ThrowAddingDuplicateWithKeyArgumentException(object key) + { + throw GetAddingDuplicateWithKeyArgumentException(key); + } + + internal static void ThrowKeyNotFoundException() + { + throw new KeyNotFoundException(); + } + internal static void ThrowArgumentException(ExceptionResource resource) { throw new ArgumentException(GetResourceString(resource)); @@ -121,15 +155,6 @@ namespace System throw new ArgumentException(GetResourceString(ExceptionResource.Argument_InvalidArrayType)); } - private static ArgumentException GetWrongValueTypeArgumentException(object value, Type targetType) - { - return new ArgumentException(SR.Format(SR.Arg_WrongType, value, targetType), nameof(value)); - } - internal static void ThrowWrongValueTypeArgumentException(object value, Type targetType) - { - throw GetWrongValueTypeArgumentException(value, targetType); - } - internal static void ThrowArgumentNullException(ExceptionArgument argument) { throw new ArgumentNullException(GetArgumentName(argument)); @@ -155,6 +180,10 @@ namespace System throw new InvalidOperationException(GetResourceString(ExceptionResource.InvalidOperation_EnumOpCantHappen)); } + internal static void ThrowSerializationException(ExceptionResource resource) + { + throw new SerializationException(GetResourceString(resource)); + } internal static void ThrowNotSupportedException(ExceptionResource resource) { @@ -176,14 +205,20 @@ namespace System { switch (argument) { + case ExceptionArgument.obj: + return "obj"; + case ExceptionArgument.dictionary: + return "dictionary"; case ExceptionArgument.array: return "array"; + case ExceptionArgument.info: + return "info"; + case ExceptionArgument.key: + return "key"; case ExceptionArgument.text: return "text"; case ExceptionArgument.values: return "values"; - case ExceptionArgument.obj: - return "obj"; case ExceptionArgument.value: return "value"; case ExceptionArgument.startIndex: @@ -261,6 +296,14 @@ namespace System return SR.InvalidOperation_EnumFailedVersion; case ExceptionResource.InvalidOperation_EnumOpCantHappen: return SR.InvalidOperation_EnumOpCantHappen; + case ExceptionResource.Serialization_MissingKeys: + return SR.Serialization_MissingKeys; + case ExceptionResource.Serialization_NullKey: + return SR.Serialization_NullKey; + case ExceptionResource.NotSupported_KeyCollectionSet: + return SR.NotSupported_KeyCollectionSet; + case ExceptionResource.NotSupported_ValueCollectionSet: + return SR.NotSupported_ValueCollectionSet; default: Debug.Assert(false, "The enum value is not defined, please check the ExceptionResource Enum."); @@ -274,10 +317,13 @@ namespace System // internal enum ExceptionArgument { + obj, + dictionary, array, + info, + key, text, values, - obj, value, startIndex, task, @@ -318,5 +364,9 @@ namespace System ArgumentOutOfRange_BiggerThanCollection, InvalidOperation_EnumFailedVersion, InvalidOperation_EnumOpCantHappen, + Serialization_MissingKeys, + Serialization_NullKey, + NotSupported_KeyCollectionSet, + NotSupported_ValueCollectionSet, } } -- cgit v1.2.3 From 677141424abeea1a0f710186796b74aba727f8e4 Mon Sep 17 00:00:00 2001 From: Aditya Mandaleeka Date: Mon, 6 Nov 2017 10:46:11 -0800 Subject: Implement RegDisplayToUnwindCursor for ARM. --- src/Native/Runtime/unix/UnixContext.cpp | 39 +++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/Native/Runtime/unix/UnixContext.cpp b/src/Native/Runtime/unix/UnixContext.cpp index 02d832b4c..8b9c5c1b3 100644 --- a/src/Native/Runtime/unix/UnixContext.cpp +++ b/src/Native/Runtime/unix/UnixContext.cpp @@ -197,10 +197,11 @@ #endif // __APPLE__ -// Update unw_cursor_t from REGDISPLAY -static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *cursor, bool setIp) +// Update unw_cursor_t from REGDISPLAY. +// NOTE: We don't set the IP here since the current use cases for this function +// don't require it. +static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *cursor) { -#if defined(_AMD64_) #define ASSIGN_REG(regName1, regName2) \ unw_set_reg(cursor, regName1, regDisplay->regName2); @@ -208,11 +209,7 @@ static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *curso if (regDisplay->p##regName2 != NULL) \ unw_set_reg(cursor, regName1, *(regDisplay->p##regName2)); - if (setIp) - { - ASSIGN_REG(UNW_REG_IP, IP) - } - +#if defined(_AMD64_) ASSIGN_REG(UNW_REG_SP, SP) ASSIGN_REG_PTR(UNW_X86_64_RBP, Rbp) ASSIGN_REG_PTR(UNW_X86_64_RBX, Rbx) @@ -220,10 +217,21 @@ static void RegDisplayToUnwindCursor(REGDISPLAY* regDisplay, unw_cursor_t *curso ASSIGN_REG_PTR(UNW_X86_64_R13, R13) ASSIGN_REG_PTR(UNW_X86_64_R14, R14) ASSIGN_REG_PTR(UNW_X86_64_R15, R15) +#elif _ARM_ + ASSIGN_REG(UNW_ARM_SP, SP) + ASSIGN_REG_PTR(UNW_ARM_R4, R4) + ASSIGN_REG_PTR(UNW_ARM_R5, R5) + ASSIGN_REG_PTR(UNW_ARM_R6, R6) + ASSIGN_REG_PTR(UNW_ARM_R7, R7) + ASSIGN_REG_PTR(UNW_ARM_R8, R8) + ASSIGN_REG_PTR(UNW_ARM_R9, R9) + ASSIGN_REG_PTR(UNW_ARM_R10, R10) + ASSIGN_REG_PTR(UNW_ARM_R11, R11) + ASSIGN_REG_PTR(UNW_ARM_R14, LR) +#endif #undef ASSIGN_REG #undef ASSIGN_REG_PTR -#endif // _AMD64_ } // Returns the unw_proc_info_t for a given IP. @@ -277,17 +285,17 @@ bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* curs return false; } - bool ipSetInUnwindContext = true; - -#ifdef _AMD64_ + // Set the IP here instead of after unwinder initialization. unw_init_local + // will do some initialization of internal structures based on the IP value. // We manually index into the unw_context_t's internals for now because there's // no better way to modify it. This whole function will go away in the future // when we are able to read unwind info without initializing an unwind cursor. +#ifdef _AMD64_ unwContext->data[16] = regDisplay->IP; #elif _ARM_ unwContext.data[15] = regDisplay->IP; #else - ipSetInUnwindContext = false; + #error "InitializeUnwindContextAndCursor is not supported on this arch yet." #endif st = unw_init_local(cursor, unwContext); @@ -297,10 +305,7 @@ bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* curs } // Set the unwind context to the specified Windows context. - // We skip the IP register if it was already set in the unw_context - // passed into during unw_init_local. Setting it again is not necessary - // and causes libunwind to do extra work. - RegDisplayToUnwindCursor(regDisplay, cursor, !ipSetInUnwindContext /* setIp */); + RegDisplayToUnwindCursor(regDisplay, cursor); return true; } -- cgit v1.2.3 From fcb3aa0da729c9d8a8b5909a0556f0201321d541 Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Tue, 7 Nov 2017 18:41:25 +0300 Subject: [armel] fixed PC passing to unwind --- src/Native/Runtime/unix/UnixContext.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Native/Runtime/unix/UnixContext.cpp b/src/Native/Runtime/unix/UnixContext.cpp index 8b9c5c1b3..0fdbc23ca 100644 --- a/src/Native/Runtime/unix/UnixContext.cpp +++ b/src/Native/Runtime/unix/UnixContext.cpp @@ -254,7 +254,7 @@ bool GetUnwindProcInfo(PCODE ip, unw_proc_info_t *procInfo) // LSDA and other information without initializing an unwind cursor. unwContext.data[16] = ip; #elif _ARM_ - unwContext.data[15] = ip; + ((uint32_t*)(unwContext.data))[15] = ip; #else #error "GetUnwindProcInfo is not supported on this arch yet." #endif @@ -293,7 +293,7 @@ bool InitializeUnwindContextAndCursor(REGDISPLAY* regDisplay, unw_cursor_t* curs #ifdef _AMD64_ unwContext->data[16] = regDisplay->IP; #elif _ARM_ - unwContext.data[15] = regDisplay->IP; + ((uint32_t*)(unwContext->data))[15] = regDisplay->IP; #else #error "InitializeUnwindContextAndCursor is not supported on this arch yet." #endif -- cgit v1.2.3 From 83d5a0185075f778d96e83509100bf6496389ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 7 Nov 2017 16:19:14 +0100 Subject: Remove WinRT references in S.P.Interop in non-WinRT environments --- src/System.Private.Interop/src/Interop/Interop.WinRT.Basic.cs | 4 ++++ src/System.Private.Interop/src/System.Private.Interop.csproj | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/System.Private.Interop/src/Interop/Interop.WinRT.Basic.cs b/src/System.Private.Interop/src/Interop/Interop.WinRT.Basic.cs index b265e8c16..8989c2f72 100644 --- a/src/System.Private.Interop/src/Interop/Interop.WinRT.Basic.cs +++ b/src/System.Private.Interop/src/Interop/Interop.WinRT.Basic.cs @@ -67,6 +67,7 @@ namespace System.Runtime.InteropServices public static partial class ExternalInterop { +#if ENABLE_MIN_WINRT [DllImport(Libraries.CORE_WINRT)] [McgGeneratedNativeCallCodeAttribute] [MethodImplAttribute(MethodImplOptions.NoInlining)] @@ -80,6 +81,7 @@ namespace System.Runtime.InteropServices uint length, HSTRING_HEADER* phstringHeader, void* hstring); +#endif [DllImport(Libraries.CORE_COM)] [McgGeneratedNativeCallCodeAttribute] @@ -157,6 +159,7 @@ namespace System.Runtime.InteropServices } } +#if ENABLE_MIN_WINRT internal static unsafe void RoGetActivationFactory(string className, ref Guid iid, out IntPtr ppv) { fixed (char* unsafe_className = className) @@ -186,6 +189,7 @@ namespace System.Runtime.InteropServices } } } +#endif public static unsafe int CoGetContextToken(out IntPtr ppToken) { diff --git a/src/System.Private.Interop/src/System.Private.Interop.csproj b/src/System.Private.Interop/src/System.Private.Interop.csproj index 34b57e303..a16d0f8ec 100644 --- a/src/System.Private.Interop/src/System.Private.Interop.csproj +++ b/src/System.Private.Interop/src/System.Private.Interop.csproj @@ -189,11 +189,11 @@ + - -- cgit v1.2.3 From a579f537fd48a9c8bce6929ad21852019da8e7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 7 Nov 2017 16:20:15 +0100 Subject: Remove workaround for #2601 --- src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs | 9 --------- src/Native/Bootstrap/main.cpp | 16 ++++++++++------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs b/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs index 08458b899..00954908a 100644 --- a/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs +++ b/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs @@ -96,15 +96,6 @@ namespace Internal.TypeSystem.Interop if (forceLazyResolution.HasValue) return forceLazyResolution.Value; - // In multi-module library mode, the WinRT p/invokes in System.Private.Interop cause linker failures - // since we don't link against the OS libraries containing those APIs. Force them to be lazy. - // See https://github.com/dotnet/corert/issues/2601 - string assemblySimpleName = ((IAssemblyDesc)((MetadataType)method.OwningType).Module).GetName().Name; - if (assemblySimpleName == "System.Private.Interop") - { - return true; - } - // Determine whether this call should be made through a lazy resolution or a static reference // Eventually, this should be controlled by a custom attribute (or an extension to the metadata format). if (importModule == "[MRT]" || importModule == "*") diff --git a/src/Native/Bootstrap/main.cpp b/src/Native/Bootstrap/main.cpp index bbeff8c9c..e3c1a2fc9 100644 --- a/src/Native/Bootstrap/main.cpp +++ b/src/Native/Bootstrap/main.cpp @@ -85,11 +85,12 @@ static char& __unbox_z = __stop___unbox; #endif // !CPPCODEGEN +// Do not warn that extern C methods throw exceptions. This is temporary +// as long as we have unimplemented/throwing APIs in this file. +#pragma warning(disable:4297) #ifdef CPPCODEGEN -#pragma warning(disable:4297) - extern "C" Object * RhNewObject(MethodTable * pMT); extern "C" Object * RhNewArray(MethodTable * pMT, int32_t elements); extern "C" void * RhTypeCast_IsInstanceOf(void * pObject, MethodTable * pMT); @@ -238,15 +239,18 @@ extern "C" void RhpUniversalTransition_DebugStepTailCall() { throw "RhpUniversalTransition_DebugStepTailCall"; } -extern "C" void CCWAddRef() -{ - throw "CCWAddRef"; -} void* RtRHeaderWrapper(); #endif // CPPCODEGEN +// This works around System.Private.Interop's references to Interop.Native. +// This won't be needed once we stop dragging in S.P.Interop for basic p/invoke support. +extern "C" void CCWAddRef() +{ + throw "CCWAddRef"; +} + extern "C" void __fail_fast() { // TODO: FailFast -- cgit v1.2.3 From 1e2dbfff5318e844b6290dfe9fd3fed8b693e115 Mon Sep 17 00:00:00 2001 From: Andon Andonov Date: Wed, 8 Nov 2017 00:18:45 -0800 Subject: Support for dotnet publish (#4870) Support for the dotnet publish command. Built on top of changes made by @nattress . NuGet packages that target .NETCore specifically can be published safely, but most others targeting the .NETFramework cannot. --- ...d-and-run-ilcompiler-in-console-shell-prompt.md | 10 +- .../BuildFrameworkNativeObjects.proj | 8 +- .../Microsoft.NETCore.Native.Publish.targets | 61 ++++++++ .../Microsoft.NETCore.Native.targets | 28 ++-- .../src/ComputeManagedAssemblies.cs | 168 +++++++++++++++++++++ .../src/ILCompiler.Build.Tasks.csproj | 25 +++ src/ILCompiler/ILCompiler.sln | 22 +++ 7 files changed, 305 insertions(+), 17 deletions(-) create mode 100644 src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets create mode 100644 src/ILCompiler.Build.Tasks/src/ComputeManagedAssemblies.cs create mode 100644 src/ILCompiler.Build.Tasks/src/ILCompiler.Build.Tasks.csproj diff --git a/Documentation/how-to-build-and-run-ilcompiler-in-console-shell-prompt.md b/Documentation/how-to-build-and-run-ilcompiler-in-console-shell-prompt.md index baf53f154..fdbc04d84 100644 --- a/Documentation/how-to-build-and-run-ilcompiler-in-console-shell-prompt.md +++ b/Documentation/how-to-build-and-run-ilcompiler-in-console-shell-prompt.md @@ -60,10 +60,10 @@ This approach uses the same code-generator (RyuJIT), as [CoreCLR](https://github From the shell/command prompt, issue the following commands, from the folder containing your project, to generate the native executable ``` - dotnet build /t:LinkNative + dotnet publish -r win-x64|linux-x64|osx-x64 ``` -Native executable will be dropped in `./bin/[configuration]/native/` folder and will have the same name as the folder in which your source file is present. +Native executable will be dropped in `./bin/x64/[configuration]/netcoreapp2.0/publish/` folder and will have the same name as the folder in which your source file is present. ## Using CPP Code Generator ## @@ -72,11 +72,15 @@ This approach uses [transpiler](https://en.wikipedia.org/wiki/Source-to-source_c From the shell/command prompt, issue the following commands to generate the native executable: ``` - dotnet build /t:LinkNative /p:NativeCodeGen=cpp + dotnet publish /p:NativeCodeGen=cpp -r win-x64|linux-x64|osx-x64 ``` For CoreRT debug build on Windows, add an extra `/p:AdditionalCppCompilerFlags=/MTd` argument. +## Disabling Native Compilation + +Native compilation can be disabled during publishing by adding an extra `/p:NativeCompilationDuringPublish=false` argument. + ## Workarounds for build errors on Windows ## If you are seeing errors such as: diff --git a/src/BuildIntegration/BuildFrameworkNativeObjects.proj b/src/BuildIntegration/BuildFrameworkNativeObjects.proj index 3a1f2db03..806b087f4 100644 --- a/src/BuildIntegration/BuildFrameworkNativeObjects.proj +++ b/src/BuildIntegration/BuildFrameworkNativeObjects.proj @@ -1,7 +1,7 @@ - BuildOneFrameworkLibrary + ComputeIlcCompileInputs;BuildOneFrameworkLibrary BuildAllFrameworkLibrariesAsSingleLib true $(FrameworkObjPath)\ @@ -11,12 +11,12 @@ + Inputs="@(DefaultFrameworkAssemblies)" + Outputs="@(DefaultFrameworkAssemblies->'$(NativeIntermediateOutputPath)\%(Filename)$(NativeObjectExt)')"> - LibraryToCompile=%(IlcReference.Identity) + LibraryToCompile=%(DefaultFrameworkAssemblies.Identity) diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets b/src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets new file mode 100644 index 000000000..755aa647f --- /dev/null +++ b/src/BuildIntegration/Microsoft.NETCore.Native.Publish.targets @@ -0,0 +1,61 @@ + + + + $(MSBuildThisFileDirectory)..\tools\ILCompiler.Build.Tasks.dll + + + false + + + + + + + + + + + + + + + + + + <_NativeIntermediateAssembly Include="@(IntermediateAssembly->'$(NativeOutputPath)%(Filename)$(NativeBinaryExt)')" /> + + + + + + + + + + + + + + + + + diff --git a/src/BuildIntegration/Microsoft.NETCore.Native.targets b/src/BuildIntegration/Microsoft.NETCore.Native.targets index 6e522fe48..22dd34fb0 100644 --- a/src/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/BuildIntegration/Microsoft.NETCore.Native.targets @@ -19,6 +19,7 @@ See the LICENSE file in the project root for more information. $(IntermediateOutputPath)native\ $(OutputPath)native\ + true OSX $(OS) @@ -60,20 +61,23 @@ See the LICENSE file in the project root for more information. $(FrameworkLibPath)\libframework$(LibFileExt) - - - + + Compile;ComputeIlcCompileInputs + $(IlcCompileDependsOn);BuildFrameworkLib + - - - + + - - Compile - $(IlcCompileDependsOn);BuildFrameworkLib - + + + + + + +