/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { // Convert IntPtr.Zero foreach (var node in ast.GetExpressions(AstCode.Ldsfld)) { var field = (XFieldReference)node.Operand; if (field.DeclaringType.IsIntPtr() && (field.Name == "Zero")) { node.Code = AstCode.Ldnull; node.Operand = null; node.Arguments.Clear(); node.SetType(compiler.Module.TypeSystem.Object); } } // Convert box(IntPtr) foreach (var node in ast.GetExpressions(AstCode.Box)) { var type = (XTypeReference)node.Operand; if (type.IsIntPtr()) { node.CopyFrom(node.Arguments[0]); } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { // Optimize enum2int(ldsfld(enum-const)) foreach (var node in ast.GetExpressions()) { switch (node.Code) { case AstCode.Enum_to_int: case AstCode.Enum_to_long: { var arg = node.Arguments[0]; XFieldReference fieldRef; if (arg.Match(AstCode.Ldsfld, out fieldRef)) { XFieldDefinition field; object value; if (fieldRef.TryResolve(out field) && field.IsStatic && field.DeclaringType.IsEnum && field.TryGetEnumValue(out value)) { // Replace with ldc_ix var wide = (node.Code == AstCode.Enum_to_long); node.SetCode(wide ? AstCode.Ldc_I8 : AstCode.Ldc_I4); node.Operand = wide ? (object)XConvert.ToLong(value) : XConvert.ToInt(value); node.Arguments.Clear(); } } } break; } } }
private static void ConvertBeqBneWithConstToBrtrueBrfalse(AstNode ast) { // Convert beq/bne (cxx(x,x), 0/null/false/true) expressions to brtrue/brfalse foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.__Beq || x.Code == AstCode.__Bne_Un)).Reverse()) { bool?reverse = ReverseCodeOnEqualCollapse(node); if (reverse == null) { continue; } var expr = node.Arguments[0]; if (node.Code == AstCode.__Bne_Un) { reverse = !reverse; } var code = reverse.Value ? AstCode.Brfalse : AstCode.Brtrue; var newExpr = new AstExpression(expr.SourceLocation, code, node.Operand, expr); node.CopyFrom(newExpr, true); } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions()) { switch (node.Code) { case AstCode.Stloc: { var variable = (AstVariable)node.Operand; var varType = variable.Type; var arg = node.Arguments[0]; ConvertIfNeeded(arg, varType); } break; case AstCode.Stelem_I2: { var arrayElementType = node.Arguments[0].GetResultType().ElementType; var arg = node.Arguments[2]; ConvertIfNeeded(arg, arrayElementType); } break; } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions(AstCode.Stloc)) { // Select the nodes to work on var ldCode = AstCode.Ldloc; var stArg = node.Arguments[0]; AstCode newCode; if ((stArg.Arguments.Count >= 1) && (operatorMap.TryGetValue(stArg.Code, out newCode))) { // Found matching operator var ldArg = stArg.Arguments[0]; if (ldArg.Code == ldCode) { // Found a matching 'load' opcode. if (ldArg.Operand == node.Operand) { var type = stArg.GetResultType(); if (!type.IsEnum()) { // Found matching "local" node.Code = newCode; node.SetArguments(stArg.Arguments.Skip(1)); node.SetType(type); } } } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions()) { switch (node.Code) { case AstCode.ByRefOutArray: node.Arguments.Clear(); return; case AstCode.Conv_U2: if (node.GetResultType().IsChar() && node.Arguments[0].Match(AstCode.Int_to_ushort)) { // Remove useless conversion (int_to_ushort followed by conv_u2) var value = node.Arguments[0].Arguments[0]; if (value.GetResultType().IsChar()) { node.CopyFrom(value); } else { // keep the conv_u2 but drop the int_to_ushort. // TODO:maybe there is a better way to do the conversion in one step. // Convert.ToUInt16(char) looks now like this: // .method public static ToUInt16(C)S // .registers 3 // .param p0 # C // #v0=(Uninit);p0=(Char); // int-to-char v0, p0 // #v0=(Char); // #v1=(Uninit); // const v1, 0xffff // #v1=(Char); // #v0=(Char);v1=(Char); // and-int/2addr v0, v1 // #v0=(Integer); // #v0=(Integer); // return v0 //.end method node.Arguments.Clear(); node.Arguments.Add(value); } } break; } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions()) { if (node.Code == AstCode.Ldloc) { var variable = (AstVariable)node.Operand; var varType = variable.Type; var expType = node.ExpectedType; if ((expType != null) && (!varType.IsSame(expType))) { if (varType.IsByte() && (expType.IsChar() || expType.IsUInt16())) { var ldloc = new AstExpression(node); var code = expType.IsUInt16() ? AstCode.Int_to_ushort : AstCode.Conv_U2; node.CopyFrom(new AstExpression(node.SourceLocation, code, null, ldloc).SetType(expType)); } } } else if (node.Code.IsCall()) { var methodRef = (XMethodReference)node.Operand; var returnType = methodRef.ReturnType; var expType = node.ExpectedType; if ((expType != null) && (!returnType.IsSame(expType))) { if (returnType.IsByte() && (expType.IsChar() || expType.IsUInt16())) { var ldloc = new AstExpression(node); var code = expType.IsUInt16() ? AstCode.Int_to_ushort : AstCode.Conv_U2; node.CopyFrom(new AstExpression(node.SourceLocation, code, null, ldloc).SetType(expType)); } else if (returnType.IsUInt16() && expType.IsChar()) { var ldloc = new AstExpression(node); node.CopyFrom(new AstExpression(node.SourceLocation, AstCode.Conv_U2, null, ldloc).SetType(expType)); } else if (returnType.IsChar() && expType.IsUInt16()) { var ldloc = new AstExpression(node); node.CopyFrom(new AstExpression(node.SourceLocation, AstCode.Int_to_ushort, null, ldloc).SetType(expType)); } } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions()) { if (node.Code == AstCode.Ldloc) { var variable = (AstVariable) node.Operand; var varType = variable.Type; var expType = node.ExpectedType; if ((expType != null) && (!varType.IsSame(expType))) { if (varType.IsByte() && (expType.IsChar() || expType.IsUInt16())) { var ldloc = new AstExpression(node); var code = expType.IsUInt16() ? AstCode.Int_to_ushort : AstCode.Conv_U2; node.CopyFrom(new AstExpression(node.SourceLocation, code, null, ldloc).SetType(expType)); } } } else if (node.Code.IsCall()) { var methodRef = (XMethodReference) node.Operand; var returnType = methodRef.ReturnType; var expType = node.ExpectedType; if ((expType != null) && (!returnType.IsSame(expType))) { if (returnType.IsByte() && (expType.IsChar() || expType.IsUInt16())) { var ldloc = new AstExpression(node); var code = expType.IsUInt16() ? AstCode.Int_to_ushort : AstCode.Conv_U2; node.CopyFrom(new AstExpression(node.SourceLocation, code, null, ldloc).SetType(expType)); } else if (returnType.IsUInt16() && expType.IsChar()) { var ldloc = new AstExpression(node); node.CopyFrom(new AstExpression(node.SourceLocation, AstCode.Conv_U2, null, ldloc).SetType(expType)); } else if (returnType.IsChar() && expType.IsUInt16()) { var ldloc = new AstExpression(node); node.CopyFrom(new AstExpression(node.SourceLocation, AstCode.Int_to_ushort, null, ldloc).SetType(expType)); } } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions()) { AstExpression arg1; XMethodReference method; if (node.Match(AstCode.Call, out method, out arg1) && arg1.Match(AstCode.Ldtoken)) { if ((method.Name == "GetTypeFromHandle") && method.DeclaringType.IsSystemType()) { node.Code = AstCode.TypeOf; node.Operand = arg1.Operand; node.Arguments.Clear(); node.SetType(compiler.Module.TypeSystem.Type); } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions()) { switch (node.Code) { case AstCode.ByRefOutArray: node.Arguments.Clear(); return; case AstCode.Conv_U2: if (node.GetResultType().IsChar() && node.Arguments[0].Match(AstCode.Int_to_ushort)) { // Remove useless conversion (int_to_ushort followed by conv_u2) var value = node.Arguments[0].Arguments[0]; node.CopyFrom(value); } break; } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions()) { if ((node.Code == AstCode.Conv_U8) && (node.Arguments[0].Code == AstCode.Ldc_I4)) { var value = node.Arguments[0].Operand; node.Code = AstCode.Ldc_I8; node.Arguments.Clear(); node.Operand = XConvert.ToLong(value) & 0xFFFFFFFFL; } else if ((node.Code == AstCode.Ldc_I4) || (node.Code == AstCode.Ldc_R4)) { var type = node.GetResultType(); if (type.IsWide()) { node.Code = (node.Code == AstCode.Ldc_I4) ? AstCode.Ldc_I8 : AstCode.Ldc_R8; } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { // Optimize brtrue (cxx(int, int)) expressions foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Brtrue) || (x.Code == AstCode.Brfalse))) { var expr = node.Arguments[0]; if (expr.Code.IsCompare() && (expr.Arguments.Count == 2)) { var arg1 = expr.Arguments[0]; var arg2 = expr.Arguments[1]; if (arg1.IsInt32() && arg2.IsInt32()) { // Simplify var code = (node.Code == AstCode.Brtrue) ? expr.Code : expr.Code.Reverse(); var newExpr = new AstExpression(node.SourceLocation, code.ToBranch(), node.Operand, arg1, arg2); node.CopyFrom(newExpr); } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { // Optimize brtrue (cxx(int, int)) expressions foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Brtrue) || (x.Code == AstCode.Brfalse))) { var expr = node.Arguments[0]; if (expr.Code.IsCompare() && (expr.Arguments.Count == 2)) { var arg1 = expr.Arguments[0]; var arg2 = expr.Arguments[1]; if (arg1.IsInt32() && arg2.IsInt32()) { // Simplify var code = (node.Code == AstCode.Brtrue) ? expr.Code : expr.Code.Reverse(); var newExpr = new AstExpression(expr.SourceLocation, code.ToBranch(), node.Operand, arg1, arg2); node.CopyFrom(newExpr, true); } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions()) { switch (node.Code) { case AstCode.Stloc: { var variable = (AstVariable)node.Operand; var varType = variable.Type; var arg = node.Arguments[0]; ConvertIfNeeded(arg, varType); } break; case AstCode.Ret: { // TODO: whats the difference between this and RLBuilder.ConvertTypeBeforeStore? if (node.Arguments.Count > 0) { var varType = node.Arguments[0].ExpectedType; if (varType != null) { var arg = node.Arguments[0]; ConvertForRetIfNeeded(arg, varType); } } } break; case AstCode.Stelem_I2: { var arrayElementType = node.Arguments[0].GetResultType().ElementType; var arg = node.Arguments[2]; ConvertIfNeeded(arg, arrayElementType); } break; } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, MethodSource currentMethod, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Call) || (x.Code == AstCode.Calli) || (x.Code == AstCode.Callvirt))) { var method = (XMethodReference)node.Operand; XMethodDefinition methodDef; if (method.TryResolve(out methodDef) && methodDef.IsConstructor && methodDef.DeclaringType.IsPrimitive && (node.Arguments.Count == 2)) { // primitive.ctor(addressof_primitive, value) -> primitive.cast(value) var locVar = node.Arguments[0].Operand; node.Arguments.RemoveAt(0); node.SetCode(AstCode.Stloc).SetType(node.Arguments[0].GetResultType()).Operand = locVar; } else { for (var i = 0; i < node.Arguments.Count; i++) { ProcessArgument(node, method, i, currentMethod, compiler.Module); } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions(AstCode.Newobj)) { var method = (XMethodReference)node.Operand; if (method.Name == ".ctor") { XTypeDefinition declaringType; if (method.DeclaringType.TryResolve(out declaringType) && declaringType.IsDelegate()) { var ldftn = node.Arguments[1]; if ((ldftn.Code == AstCode.Ldftn) || (ldftn.Code == AstCode.Ldvirtftn)) { var token = (XMethodReference)ldftn.Operand; node.Code = AstCode.Delegate; node.Operand = Tuple.Create(declaringType, token.GetElementMethod().Resolve()); node.Arguments.RemoveAt(1); } } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions(AstCode.Newobj)) { var method = (XMethodReference) node.Operand; if (method.Name == ".ctor") { XTypeDefinition declaringType; if (method.DeclaringType.TryResolve(out declaringType) && declaringType.IsDelegate()) { var ldftn = node.Arguments[1]; if ((ldftn.Code == AstCode.Ldftn) || (ldftn.Code == AstCode.Ldvirtftn)) { var token = (XMethodReference) ldftn.Operand; node.Code = AstCode.Delegate; node.Operand = Tuple.Create(declaringType, token.GetElementMethod().Resolve()); node.Arguments.RemoveAt(1); } } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast) { foreach (var node in ast.GetExpressions(AstCode.Cgt_Un)) { if ((node.Arguments.Count == 2) && (node.Arguments[1].Code == AstCode.Ldnull)) { if (node.Arguments[0].Code == AstCode.Isinst) { // Cgt_un(IsInst(x), ldnull) -> InstanceOf(x) var argX = node.Arguments[0].Arguments[0]; node.Operand = node.Arguments[0].Operand; node.Code = AstCode.InstanceOf; node.Arguments.Clear(); node.Arguments.Add(argX); } else { // Cgt_un(x, ldnull) -> CIsNotNull(x) node.Arguments.RemoveAt(1); node.Code = AstCode.CIsNotNull; } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions(AstCode.Call)) { var method = node.Operand as XMethodReference; if ((method != null) && method.DeclaringType.GetElementType().IsNullableT()) { var git = (XGenericInstanceType)method.DeclaringType; var type = git.GenericArguments[0]; if ((node.Arguments.Count == 2) && (method.Name == ".ctor")) { var target = node.Arguments[0]; var value = node.Arguments[1]; if (type.IsPrimitive) { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertPrimitiveCtor(node, method, type, target, value); break; } } else { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertOtherCtor(node, type, target, value); break; } } } else if ((node.Arguments.Count == 1) && (method.Name == "get_HasValue")) { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: ConvertHasValue(node, type, target); break; } } else if ((node.Arguments.Count == 1) && (method.Name == "get_Value")) { if (type.IsPrimitive) { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: ConvertPrimitiveGetValue(node, method, type, target, compiler.Module); break; } } else { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: ConvertOtherGetValue(node, method, type, target, compiler.Module); break; } } } else if ((node.Arguments.Count == 1) && (method.Name == "GetValueOrDefault")) { if (type.IsPrimitive) { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: ConvertPrimitiveGetValueOrDefault(node, method, type, target, compiler.Module); break; } } else { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: ConvertOtherGetValueOrDefault(node, type); break; } } } else if ((node.Arguments.Count == 1) && (method.Name == "get_RawValue")) { var target = node.Arguments[0]; if (type.IsPrimitive) { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertPrimitiveGetRawValue(node, type, target); break; } } else { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertOtherGetRawValue(node, type, target); break; } } } else if (type.IsPrimitive || type.IsEnum()) { // Convert ld_a to ld foreach (var target in node.Arguments) { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertLoad(target); break; } } } } } foreach (var node in ast.GetExpressions(AstCode.Newobj)) { var method = node.Operand as XMethodReference; if ((method != null) && method.DeclaringType.GetElementType().IsNullableT()) { var git = (XGenericInstanceType)method.DeclaringType; var type = git.GenericArguments[0]; if ((node.Arguments.Count == 1) && (method.Name == ".ctor")) { var value = node.Arguments[0]; if (type.IsPrimitive) { ConvertPrimitiveNewObj(node, method, type, value); } else { ConvertOtherNewObj(node, method, type, value); } } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { var typeSystem = compiler.Module.TypeSystem; foreach (var node in ast.GetExpressions().Reverse()) { if ((node.Code == AstCode.Ldc_I4) || (node.Code == AstCode.Ldc_I8)) { var typeRef = node.InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { // Convert to ld-enum-value XFieldDefinition field; if (typeDef.TryGetEnumConstField(node.Operand, out field)) { // Convert to ldsfld node.Code = AstCode.Ldsfld; node.Operand = field; node.InferredType = typeDef; node.ExpectedType = typeDef; } else { // Call Enum.GetValue(enumType, value) var module = compiler.Module; var valueType = (node.Code == AstCode.Ldc_I8) ? module.TypeSystem.Long : module.TypeSystem.Int; var value = new AstExpression(node) { InferredType = valueType, ExpectedType = valueType }; node.Code = (node.Code == AstCode.Ldc_I8) ? AstCode.Long_to_enum : AstCode.Int_to_enum; node.Operand = null; node.InferredType = typeDef; node.ExpectedType = typeDef; node.Arguments.Clear(); node.Arguments.Add(value); } } } else if (node.Code.IsBinaryOperation()) { var typeRef = node.InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { ConvertBinOp(node, typeDef, compiler); continue; // Avoid recursion } } else if (node.Code.IsIntegerOnlyCompare()) { var typeRef = node.Arguments[0].InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { ConvertICmpArgument(node, 0, typeDef, compiler); } typeRef = node.Arguments[1].InferredType; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { ConvertICmpArgument(node, 1, typeDef, compiler); } } else if ((node.Code == AstCode.Switch) || (node.Code == AstCode.LookupSwitch)) { var typeRef = node.Arguments[0].InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { var isWide = typeDef.GetEnumUnderlyingType().IsWide(); var enumValue = new AstExpression(node.Arguments[0]); var numericValue = new AstExpression(node.SourceLocation, isWide ? AstCode.Enum_to_long : AstCode.Enum_to_int, null); numericValue.InferredType = isWide ? typeSystem.Long : typeSystem.Int; numericValue.Arguments.Add(enumValue); node.Arguments.Clear(); node.Arguments.Add(numericValue); } } else if (node.Code == AstCode.Brfalse) { // brfalse(enum-expr) is used as "enum-expr == 0-valued-enum-value" var typeRef = node.Arguments[0].InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { var isWide = typeDef.GetEnumUnderlyingType().IsWide(); var enumValue = new AstExpression(node.Arguments[0]); var numericValue = new AstExpression(node.SourceLocation, isWide ? AstCode.Enum_to_long : AstCode.Enum_to_int, null); numericValue.InferredType = isWide ? typeSystem.Long : typeSystem.Int; numericValue.Arguments.Add(enumValue); node.Arguments.Clear(); node.Arguments.Add(numericValue); } } // Note: no else here { // Need to value type? var inferredType = node.InferredType; var expectedType = node.ExpectedType; if ((inferredType != null) && (expectedType != null) && !inferredType.IsSame(expectedType)) { // don't convert if either one is the abstract base class. if (inferredType.IsInternalEnum() || expectedType.IsInternalEnum()) { continue; } // don't convert if either one is a reference type if (inferredType.IsByReference || expectedType.IsByReference) { continue; } XTypeDefinition expectedTypeDef; if (expectedType.TryResolve(out expectedTypeDef) && expectedTypeDef.IsEnum) { // Enum expected, non-enum or different enum found var underlyingType = expectedTypeDef.GetEnumUnderlyingType(); var isWide = underlyingType.IsWide(); // If inferred type is another enum, convert to primitive first if (inferredType.IsEnum()) { var toPrimitive = new AstExpression(node) { ExpectedType = null }; node.Code = isWide ? AstCode.Enum_to_long : AstCode.Enum_to_int; node.SetArguments(toPrimitive); node.Operand = null; node.SetType(isWide ? typeSystem.Long : typeSystem.Int); } else if (inferredType.IsPrimitive) { // Convert float/double to int/long before converting to enum if (inferredType.IsDouble() || inferredType.IsFloat()) { var code = isWide ? AstCode.Conv_I8 : AstCode.Conv_I4; var clone = new AstExpression(node) { ExpectedType = null }; node.Code = code; node.SetArguments(clone); node.Operand = null; node.SetType(underlyingType); } } var actualValue = new AstExpression(node) { ExpectedType = null }; node.Code = isWide ? AstCode.Long_to_enum : AstCode.Int_to_enum; node.Operand = null; node.SetArguments(actualValue); node.SetType(expectedTypeDef); } else { XTypeDefinition inferredTypeDef; if (inferredType.TryResolve(out inferredTypeDef) && inferredTypeDef.IsEnum) { if (!expectedType.IsSystemObject()) { // Enum found, non-enum expected var underlyingType = inferredTypeDef.GetEnumUnderlyingType(); var isWide = underlyingType.IsWide(); var actualValue = new AstExpression(node) { ExpectedType = null }; node.Code = isWide ? AstCode.Enum_to_long : AstCode.Enum_to_int; node.Operand = null; node.SetArguments(actualValue); node.SetType(isWide ? typeSystem.Long : typeSystem.Int); ConvertIfNeeded(node, underlyingType); } } } } } } // Convert value of switch node foreach (var node in ast.GetSelfAndChildrenRecursive <AstSwitch>()) { var typeRef = node.Condition.InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { var isWide = typeDef.GetEnumUnderlyingType().IsWide(); var toValue = new AstExpression(node.Condition.SourceLocation, isWide ? AstCode.Enum_to_long : AstCode.Enum_to_int, null); toValue.InferredType = isWide ? typeSystem.Long : typeSystem.Int; toValue.Arguments.Add(node.Condition); node.Condition = toValue; } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions()) { switch (node.Code) { // Optimize enum2int(ldsfld(enum-const)) // and enum2int(int.to.enum(xxx)) case AstCode.Enum_to_int: case AstCode.Enum_to_long: { var arg = node.Arguments[0]; XFieldReference fieldRef; if (arg.Match(AstCode.Ldsfld, out fieldRef)) { XFieldDefinition field; object value; if (fieldRef.TryResolve(out field) && field.IsStatic && field.DeclaringType.IsEnum && field.TryGetEnumValue(out value)) { // Replace with ldc_ix var wide = (node.Code == AstCode.Enum_to_long); node.SetCode(wide ? AstCode.Ldc_I8 : AstCode.Ldc_I4); node.Operand = wide ? (object)XConvert.ToLong(value) : XConvert.ToInt(value); node.Arguments.Clear(); } } else if (arg.Code == AstCode.Int_to_enum || arg.Code == AstCode.Long_to_enum) { var expectedType = node.ExpectedType; node.CopyFrom(arg.Arguments[0]); node.ExpectedType = expectedType; } } break; // optimize ceq/cne (int/long-to-enum(int), yyy) case AstCode.Ceq: case AstCode.Cne: { if (node.Arguments.Any(a=>IsToEnum(a.Code))) { // xx_to_enum is a quite costly operation when compared to enum-to-int, // so convert this to an interger-only compare. bool isLong = node.Arguments.Any(a => a.Code == AstCode.Long_to_enum); foreach (var arg in node.Arguments) { if (IsToEnum(arg.Code)) { arg.CopyFrom(arg.Arguments[0]); arg.ExpectedType = isLong ? compiler.Module.TypeSystem.Long : compiler.Module.TypeSystem.Int; } else { Debug.Assert(arg.GetResultType().IsEnum()); var orig = new AstExpression(arg); var convert = new AstExpression(arg.SourceLocation, isLong ? AstCode.Enum_to_long : AstCode.Enum_to_int, null, orig); convert.ExpectedType = isLong ? compiler.Module.TypeSystem.Long : compiler.Module.TypeSystem.Int; arg.CopyFrom(convert); } } } break; } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions(AstCode.Call)) { var method = node.Operand as XMethodReference; if ((method != null) && method.DeclaringType.GetElementType().IsNullableT()) { var git = (XGenericInstanceType)method.DeclaringType; var type = git.GenericArguments[0]; if ((node.Arguments.Count == 2) && (method.Name == ".ctor")) { var target = node.Arguments[0]; var value = node.Arguments[1]; if (type.IsPrimitive) { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertPrimitiveCtor(node, method, type, target, value); break; } } else { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertOtherCtor(node, type, target, value); break; } } } else if ((node.Arguments.Count == 1) && (method.Name == "get_HasValue")) { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: case AstCode.Ldelem_Ref: ConvertHasValue(node, type, target); break; } } else if ((node.Arguments.Count == 1) && (method.Name == "get_Value")) { if (type.IsPrimitive) { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: case AstCode.Ldelem_Ref: ConvertPrimitiveGetValue(node, method, type, target, compiler.Module); break; } } else { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: case AstCode.Ldelem_Ref: ConvertOtherGetValue(node, method, type, target, compiler.Module); break; } } } else if (method.Name == "GetValueOrDefault" && (node.Arguments.Count == 1 || node.Arguments.Count == 2)) { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: case AstCode.Ldelem_Ref: { ConvertGetValueOrDefault(node, type, compiler.Module); } break; } } else if ((node.Arguments.Count == 1) && (method.Name == "get_RawValue")) { var target = node.Arguments[0]; if (type.IsPrimitive) { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertPrimitiveGetRawValue(node, type, target); break; } } else { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertOtherGetRawValue(node, type, target); break; } } } else if (type.IsPrimitive || type.IsEnum()) { // Convert ld_a to ld foreach (var target in node.Arguments) { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertLoad(target); break; } } } } } foreach (var node in ast.GetExpressions(AstCode.Callvirt)) { var method = node.Operand as XMethodReference; if ((method != null) && method.DeclaringType.IsSystemObject() && method.Name == "ToString" && method.Parameters.Count == 0 && node.Arguments.Count == 1 && !method.Resolve().IsStatic) { var type = node.Arguments[0].InferredType; if (type != null && type.GetElementType().IsNullableT()) { // redirect to Nullable.ToStringChecked() implementation node.Operand = compiler.GetDot42InternalType("System", "Nullable").Resolve() .Methods.First(m => m.Name == "ToStringChecked" && m.IsStatic && m.Parameters.Count == 1); node.Code = AstCode.Call; } } } foreach (var node in ast.GetExpressions(AstCode.Newobj)) { var method = node.Operand as XMethodReference; if ((method != null) && method.DeclaringType.GetElementType().IsNullableT()) { var git = (XGenericInstanceType)method.DeclaringType; var type = git.GenericArguments[0]; if ((node.Arguments.Count == 1) && (method.Name == ".ctor")) { var value = node.Arguments[0]; if (type.IsPrimitive) { ConvertPrimitiveNewObj(node, method, type, value); } else { ConvertOtherNewObj(node, method, type, value); } } } } foreach (var node in ast.GetExpressions(AstCode.TypeOf)) { var typeRef = node.Operand as XTypeReference; if (typeRef == null || !typeRef.IsGenericInstance) continue; if (!typeRef.GetElementType().IsSystemNullable()) continue; var git = (XGenericInstanceType)typeRef; if (git.GenericArguments.Count != 1) // should not happen, but don't bother. continue; var type = git.GenericArguments[0]; node.Operand = type; node.Code = type.IsPrimitive ? AstCode.BoxedTypeOf : AstCode.NullableTypeOf; } }
private static void ConvertBeqBneWithConstToBrtrueBrfalse(AstNode ast) { // Convert beq/bne (cxx(x,x), 0/null/false/true) expressions to brtrue/brfalse foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.__Beq || x.Code == AstCode.__Bne_Un)).Reverse()) { bool? reverse = ReverseCodeOnEqualCollapse(node); if (reverse == null) continue; var expr = node.Arguments[0]; if (node.Code == AstCode.__Bne_Un) reverse = !reverse; var code = reverse.Value ? AstCode.Brfalse : AstCode.Brtrue; var newExpr = new AstExpression(expr.SourceLocation, code, node.Operand, expr); node.CopyFrom(newExpr, true); } }
public static void Convert(AstNode ast, AssemblyCompiler compiler) { // combine ceq/cne (cxx(x,x), 0/null/false/true) expressions var booleanType = compiler.Module.TypeSystem.Bool; foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Ceq || x.Code == AstCode.Cne)).Reverse()) { var reverse = InvertComparisonIfPullComparisonUpToEquals(node, PullTarget.Comparison); if (reverse == null) continue; if (node.Code == AstCode.Cne) reverse = !reverse; var expr = node.Arguments[0]; AstCode code = reverse.Value ? ReverseCode(expr.Code, expr.Arguments[0].GetResultType()) : expr.Code; var newExpr = new AstExpression(expr.SourceLocation, code, node.Operand, expr.Arguments[0], expr.Arguments[1]) .SetType(booleanType); node.CopyFrom(newExpr, true); } // Convert beq/bne (cxx(x,x), 0/null/false/true) expressions to brtrue/brfalse ConvertBeqBneWithConstToBrtrueBrfalse(ast); // combine brtrue/brfalse (cxx(x,x)) expressions foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Brtrue) || (x.Code == AstCode.Brfalse))) { var expr = node.Arguments[0]; if (expr.Code.IsCompare() && (expr.Arguments.Count == 2)) { var arg1 = expr.Arguments[0]; var arg2 = expr.Arguments[1]; var arg1Type = arg1.GetResultType(); var arg2Type = arg2.GetResultType(); if (CanPullComparisonUp(node.Code, arg1Type, arg2Type, PullTarget.Branch)) { // Simplify var code = (node.Code == AstCode.Brtrue) ? expr.Code : expr.Code.Reverse(); var newExpr = new AstExpression(expr.SourceLocation, code.ToBranch(), node.Operand, arg1, arg2); node.CopyFrom(newExpr, true); } else if (CanPullComparisonUp(node.Code, arg1Type, arg2Type, PullTarget.Comparison)) { // must be double/long/float argType // simplify var code = (node.Code == AstCode.Brtrue) ? expr.Code : ReverseCode(expr.Code, arg1Type); bool needLBias = code == AstCode.Cgt || code == AstCode.Cge || code == AstCode.Cle_Un || code == AstCode.Clt_Un; var cmpCode = arg1Type.IsFloat() || arg1Type.IsDouble() ? (needLBias? AstCode.CmpLFloat : AstCode.CmpGFloat) : AstCode.CmpLong; var newExpr = new AstExpression(expr.SourceLocation, code.ToBranchZ(), node.Operand, new AstExpression(expr.SourceLocation, cmpCode, null, arg1, arg2)); node.CopyFrom(newExpr, true); } } } // Convert beq/bne (cxx(x,x), 0/null/false/true) expressions to brtrue/brfalse // again. ConvertBeqBneWithConstToBrtrueBrfalse(ast); }
public static void Convert(AstNode ast, AssemblyCompiler compiler) { // combine ceq/cne (cxx(x,x), 0/null/false/true) expressions var booleanType = compiler.Module.TypeSystem.Bool; foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Ceq || x.Code == AstCode.Cne)).Reverse()) { var reverse = InvertComparisonIfPullComparisonUpToEquals(node, PullTarget.Comparison); if (reverse == null) { continue; } if (node.Code == AstCode.Cne) { reverse = !reverse; } var expr = node.Arguments[0]; AstCode code = reverse.Value ? ReverseCode(expr.Code, expr.Arguments[0].GetResultType()) : expr.Code; var newExpr = new AstExpression(expr.SourceLocation, code, node.Operand, expr.Arguments[0], expr.Arguments[1]) .SetType(booleanType); node.CopyFrom(newExpr, true); } // Convert beq/bne (cxx(x,x), 0/null/false/true) expressions to brtrue/brfalse ConvertBeqBneWithConstToBrtrueBrfalse(ast); // combine brtrue/brfalse (cxx(x,x)) expressions foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Brtrue) || (x.Code == AstCode.Brfalse))) { var expr = node.Arguments[0]; if (expr.Code.IsCompare() && (expr.Arguments.Count == 2)) { var arg1 = expr.Arguments[0]; var arg2 = expr.Arguments[1]; var arg1Type = arg1.GetResultType(); var arg2Type = arg2.GetResultType(); if (CanPullComparisonUp(node.Code, arg1Type, arg2Type, PullTarget.Branch)) { // Simplify var code = (node.Code == AstCode.Brtrue) ? expr.Code : expr.Code.Reverse(); var newExpr = new AstExpression(expr.SourceLocation, code.ToBranch(), node.Operand, arg1, arg2); node.CopyFrom(newExpr, true); } else if (CanPullComparisonUp(node.Code, arg1Type, arg2Type, PullTarget.Comparison)) { // must be double/long/float argType // simplify var code = (node.Code == AstCode.Brtrue) ? expr.Code : ReverseCode(expr.Code, arg1Type); bool needLBias = code == AstCode.Cgt || code == AstCode.Cge || code == AstCode.Cle_Un || code == AstCode.Clt_Un; var cmpCode = arg1Type.IsFloat() || arg1Type.IsDouble() ? (needLBias? AstCode.CmpLFloat : AstCode.CmpGFloat) : AstCode.CmpLong; var newExpr = new AstExpression(expr.SourceLocation, code.ToBranchZ(), node.Operand, new AstExpression(expr.SourceLocation, cmpCode, null, arg1, arg2)); node.CopyFrom(newExpr, true); } } } // Convert beq/bne (cxx(x,x), 0/null/false/true) expressions to brtrue/brfalse // again. ConvertBeqBneWithConstToBrtrueBrfalse(ast); }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, MethodSource currentMethod, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Call) || (x.Code == AstCode.Calli) || (x.Code == AstCode.Callvirt))) { var method = (XMethodReference)node.Operand; bool processed = false; XMethodDefinition methodDef; if (method.TryResolve(out methodDef) && methodDef.DeclaringType.IsPrimitive) { if (methodDef.IsConstructor && (node.Arguments.Count == 2)) { // primitive.ctor(addressof_primitive, value) -> primitive.cast(value) var locVar = node.Arguments[0].Operand; node.Arguments.RemoveAt(0); node.SetCode(AstCode.Stloc).SetType(node.Arguments[0].GetResultType()).Operand = locVar; processed = true; } else if (methodDef.Name == "GetHashCode" && node.Arguments.Count == 1 && methodDef.HasThis) { // we try to avoid boxing. var arg = node.Arguments[0]; switch (arg.Code) { case AstCode.AddressOf: arg = arg.Arguments[0]; break; case AstCode.Ldloca: arg.SetCode(AstCode.Ldloc); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldflda: arg.SetCode(AstCode.Ldfld); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldsflda: arg.SetCode(AstCode.Ldsfld); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldelema: arg.SetCode(AstCode.Ldelem_Any); arg.SetType(arg.InferredType.ElementType); break; } var type = arg.GetResultType(); if (type.IsBoolean() || type.IsFloat() || type.IsDouble() || type.IsInt64() || type.IsUInt64()) { var ch = compiler.GetDot42InternalType("CompilerHelper").Resolve(); // these need special handling var replMethod = ch.Methods.FirstOrDefault(m => m.Name == "GetHashCode" && m.IsStatic && m.Parameters[0].ParameterType.IsSame(type, true)); if (replMethod != null) { node.Operand = replMethod; node.SetCode(AstCode.Call); node.Arguments.Clear(); node.Arguments.Add(arg); } } else { // for the other primitive types, we just return the value itself. node.CopyFrom(arg); node.ExpectedType = compiler.Module.TypeSystem.Int; } processed = true; } } if(!processed) { for (var i = 0; i < node.Arguments.Count; i++) { ProcessArgument(node, method, i, currentMethod, compiler.Module); } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, MethodSource currentMethod, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions(x => (x.Code == AstCode.Call) || (x.Code == AstCode.Calli) || (x.Code == AstCode.Callvirt))) { var method = (XMethodReference)node.Operand; bool processed = false; XMethodDefinition methodDef; if (method.TryResolve(out methodDef) && methodDef.DeclaringType.IsPrimitive) { if (methodDef.IsConstructor && (node.Arguments.Count == 2)) { // primitive.ctor(addressof_primitive, value) -> primitive.cast(value) var locVar = node.Arguments[0].Operand; node.Arguments.RemoveAt(0); node.SetCode(AstCode.Stloc).SetType(node.Arguments[0].GetResultType()).Operand = locVar; processed = true; } else if (methodDef.Name == "GetHashCode" && node.Arguments.Count == 1 && methodDef.HasThis) { // we try to avoid boxing. var arg = node.Arguments[0]; switch (arg.Code) { case AstCode.AddressOf: arg = arg.Arguments[0]; break; case AstCode.Ldloca: arg.SetCode(AstCode.Ldloc); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldflda: arg.SetCode(AstCode.Ldfld); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldsflda: arg.SetCode(AstCode.Ldsfld); arg.SetType(arg.InferredType.ElementType); break; case AstCode.Ldelema: arg.SetCode(AstCode.Ldelem_Any); arg.SetType(arg.InferredType.ElementType); break; } var type = arg.GetResultType(); if (type.IsBoolean() || type.IsFloat() || type.IsDouble() || type.IsInt64() || type.IsUInt64()) { var ch = compiler.GetDot42InternalType("CompilerHelper").Resolve(); // these need special handling var replMethod = ch.Methods.FirstOrDefault(m => m.Name == "GetHashCode" && m.IsStatic && m.Parameters[0].ParameterType.IsSame(type, true)); if (replMethod != null) { node.Operand = replMethod; node.SetCode(AstCode.Call); node.Arguments.Clear(); node.Arguments.Add(arg); } } else { // for the other primitive types, we just return the value itself. node.CopyFrom(arg); node.ExpectedType = compiler.Module.TypeSystem.Int; } processed = true; } } if (!processed) { for (var i = 0; i < node.Arguments.Count; i++) { ProcessArgument(node, method, i, currentMethod, compiler.Module); } } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions(AstCode.Call)) { var method = node.Operand as XMethodReference; if ((method != null) && method.DeclaringType.GetElementType().IsNullableT()) { var git = (XGenericInstanceType)method.DeclaringType; var type = git.GenericArguments[0]; if ((node.Arguments.Count == 2) && (method.Name == ".ctor")) { var target = node.Arguments[0]; var value = node.Arguments[1]; if (type.IsPrimitive) { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertPrimitiveCtor(node, method, type, target, value); break; } } else { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertOtherCtor(node, type, target, value); break; } } } else if ((node.Arguments.Count == 1) && (method.Name == "get_HasValue")) { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: case AstCode.Ldelem_Ref: ConvertHasValue(node, type, target); break; } } else if ((node.Arguments.Count == 1) && (method.Name == "get_Value")) { if (type.IsPrimitive) { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: case AstCode.Ldelem_Ref: ConvertPrimitiveGetValue(node, method, type, target, compiler.Module); break; } } else { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: case AstCode.Ldelem_Ref: ConvertOtherGetValue(node, method, type, target, compiler.Module); break; } } } else if (method.Name == "GetValueOrDefault" && (node.Arguments.Count == 1 || node.Arguments.Count == 2)) { var target = node.Arguments[0]; switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: case AstCode.Ldloc: case AstCode.Ldfld: case AstCode.Ldsfld: case AstCode.Ldelem_Ref: { ConvertGetValueOrDefault(node, type, compiler.Module); } break; } } else if ((node.Arguments.Count == 1) && (method.Name == "get_RawValue")) { var target = node.Arguments[0]; if (type.IsPrimitive) { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertPrimitiveGetRawValue(node, type, target); break; } } else { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertOtherGetRawValue(node, type, target); break; } } } else if (type.IsPrimitive || type.IsEnum()) { // Convert ld_a to ld foreach (var target in node.Arguments) { switch (target.Code) { case AstCode.Ldloca: case AstCode.Ldflda: case AstCode.Ldsflda: ConvertLoad(target); break; } } } } } foreach (var node in ast.GetExpressions(AstCode.Callvirt)) { var method = node.Operand as XMethodReference; if ((method != null) && method.DeclaringType.IsSystemObject() && method.Name == "ToString" && method.Parameters.Count == 0 && node.Arguments.Count == 1 && !method.Resolve().IsStatic) { var type = node.Arguments[0].InferredType; if (type != null && type.GetElementType().IsNullableT()) { // redirect to Nullable.ToStringChecked() implementation node.Operand = compiler.GetDot42InternalType("System", "Nullable").Resolve() .Methods.First(m => m.Name == "ToStringChecked" && m.IsStatic && m.Parameters.Count == 1); node.Code = AstCode.Call; } } } foreach (var node in ast.GetExpressions(AstCode.Newobj)) { var method = node.Operand as XMethodReference; if ((method != null) && method.DeclaringType.GetElementType().IsNullableT()) { var git = (XGenericInstanceType)method.DeclaringType; var type = git.GenericArguments[0]; if ((node.Arguments.Count == 1) && (method.Name == ".ctor")) { var value = node.Arguments[0]; if (type.IsPrimitive) { ConvertPrimitiveNewObj(node, method, type, value); } else { ConvertOtherNewObj(node, method, type, value); } } } } foreach (var node in ast.GetExpressions(AstCode.TypeOf)) { var typeRef = node.Operand as XTypeReference; if (typeRef == null || !typeRef.IsGenericInstance) { continue; } if (!typeRef.GetElementType().IsSystemNullable()) { continue; } var git = (XGenericInstanceType)typeRef; if (git.GenericArguments.Count != 1) // should not happen, but don't bother. { continue; } var type = git.GenericArguments[0]; node.Operand = type; node.Code = type.IsPrimitive ? AstCode.BoxedTypeOf : AstCode.NullableTypeOf; } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { var typeSystem = compiler.Module.TypeSystem; foreach (var node in ast.GetExpressions().Reverse()) { if ((node.Code == AstCode.Ldc_I4) || (node.Code == AstCode.Ldc_I8)) { var typeRef = node.InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { // Convert to ld-enum-value XFieldDefinition field; if (typeDef.TryGetEnumConstField(node.Operand, out field)) { // Convert to ldsfld node.Code = AstCode.Ldsfld; node.Operand = field; node.InferredType = typeDef; node.ExpectedType = typeDef; } else { // Call Enum.GetValue(enumType, value) var module = compiler.Module; var valueType = (node.Code == AstCode.Ldc_I8) ? module.TypeSystem.Long : module.TypeSystem.Int; var value = new AstExpression(node) {InferredType = valueType, ExpectedType = valueType}; node.Code = (node.Code == AstCode.Ldc_I8) ? AstCode.Long_to_enum : AstCode.Int_to_enum; node.Operand = null; node.InferredType = typeDef; node.ExpectedType = typeDef; node.Arguments.Clear(); node.Arguments.Add(value); } } } else if (node.Code.IsBinaryOperation()) { var typeRef = node.InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { ConvertBinOp(node, typeDef, compiler); continue; // Avoid recursion } } else if (node.Code.IsIntegerOnlyCompare()) { var typeRef = node.Arguments[0].InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { ConvertICmpArgument(node, 0, typeDef, compiler); } typeRef = node.Arguments[1].InferredType; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { ConvertICmpArgument(node, 1, typeDef, compiler); } } else if ((node.Code == AstCode.Switch) || (node.Code == AstCode.LookupSwitch)) { var typeRef = node.Arguments[0].InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { var isWide = typeDef.GetEnumUnderlyingType().IsWide(); var enumValue = new AstExpression(node.Arguments[0]); var numericValue = new AstExpression(node.SourceLocation, isWide ? AstCode.Enum_to_long : AstCode.Enum_to_int, null); numericValue.InferredType = isWide ? typeSystem.Long : typeSystem.Int; numericValue.Arguments.Add(enumValue); node.Arguments.Clear(); node.Arguments.Add(numericValue); } } else if (node.Code == AstCode.Brfalse) { // brfalse(enum-expr) is used as "enum-expr == 0-valued-enum-value" var typeRef = node.Arguments[0].InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { var isWide = typeDef.GetEnumUnderlyingType().IsWide(); var enumValue = new AstExpression(node.Arguments[0]); var numericValue = new AstExpression(node.SourceLocation, isWide ? AstCode.Enum_to_long : AstCode.Enum_to_int, null); numericValue.InferredType = isWide ? typeSystem.Long : typeSystem.Int; numericValue.Arguments.Add(enumValue); node.Arguments.Clear(); node.Arguments.Add(numericValue); } } // Note: no else here { // Need to value type? var inferredType = node.InferredType; var expectedType = node.ExpectedType; if ((inferredType != null) && (expectedType != null) && !inferredType.IsSame(expectedType)) { // don't convert if either one is the abstract base class. if (inferredType.IsInternalEnum() || expectedType.IsInternalEnum()) continue; // don't convert if either one is a reference type if (inferredType.IsByReference || expectedType.IsByReference) continue; XTypeDefinition expectedTypeDef; if (expectedType.TryResolve(out expectedTypeDef) && expectedTypeDef.IsEnum) { // Enum expected, non-enum or different enum found var underlyingType = expectedTypeDef.GetEnumUnderlyingType(); var isWide = underlyingType.IsWide(); // If inferred type is another enum, convert to primitive first if (inferredType.IsEnum()) { var toPrimitive = new AstExpression(node) { ExpectedType = null }; node.Code = isWide ? AstCode.Enum_to_long : AstCode.Enum_to_int; node.SetArguments(toPrimitive); node.Operand = null; node.SetType(isWide ? typeSystem.Long : typeSystem.Int); } else if (inferredType.IsPrimitive) { // Convert float/double to int/long before converting to enum if (inferredType.IsDouble() || inferredType.IsFloat()) { var code = isWide ? AstCode.Conv_I8 : AstCode.Conv_I4; var clone = new AstExpression(node) { ExpectedType = null }; node.Code = code; node.SetArguments(clone); node.Operand = null; node.SetType(underlyingType); } } var actualValue = new AstExpression(node) {ExpectedType = null}; node.Code = isWide ? AstCode.Long_to_enum : AstCode.Int_to_enum; node.Operand = null; node.SetArguments(actualValue); node.SetType(expectedTypeDef); } else { XTypeDefinition inferredTypeDef; if (inferredType.TryResolve(out inferredTypeDef) && inferredTypeDef.IsEnum) { if (!expectedType.IsSystemObject()) { // Enum found, non-enum expected var underlyingType = inferredTypeDef.GetEnumUnderlyingType(); var isWide = underlyingType.IsWide(); var actualValue = new AstExpression(node) {ExpectedType = null}; node.Code = isWide ? AstCode.Enum_to_long : AstCode.Enum_to_int; node.Operand = null; node.SetArguments(actualValue); node.SetType(isWide ? typeSystem.Long : typeSystem.Int); ConvertIfNeeded(node, underlyingType); } } } } } } // Convert value of switch node foreach (var node in ast.GetSelfAndChildrenRecursive<AstSwitch>()) { var typeRef = node.Condition.InferredType; XTypeDefinition typeDef; if ((typeRef != null) && typeRef.TryResolve(out typeDef) && typeDef.IsEnum) { var isWide = typeDef.GetEnumUnderlyingType().IsWide(); var toValue = new AstExpression(node.Condition.SourceLocation, isWide ? AstCode.Enum_to_long : AstCode.Enum_to_int, null); toValue.InferredType = isWide ? typeSystem.Long : typeSystem.Int; toValue.Arguments.Add(node.Condition); node.Condition = toValue; } } }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstNode ast, AssemblyCompiler compiler) { foreach (var node in ast.GetExpressions()) { switch (node.Code) { // Optimize enum2int(ldsfld(enum-const)) // and enum2int(int.to.enum(xxx)) case AstCode.Enum_to_int: case AstCode.Enum_to_long: { var arg = node.Arguments[0]; XFieldReference fieldRef; if (arg.Match(AstCode.Ldsfld, out fieldRef)) { XFieldDefinition field; object value; if (fieldRef.TryResolve(out field) && field.IsStatic && field.DeclaringType.IsEnum && field.TryGetEnumValue(out value)) { // Replace with ldc_ix var wide = (node.Code == AstCode.Enum_to_long); node.SetCode(wide ? AstCode.Ldc_I8 : AstCode.Ldc_I4); node.Operand = wide ? (object)XConvert.ToLong(value) : XConvert.ToInt(value); node.Arguments.Clear(); } } else if (arg.Code == AstCode.Int_to_enum || arg.Code == AstCode.Long_to_enum) { var expectedType = node.ExpectedType; node.CopyFrom(arg.Arguments[0]); node.ExpectedType = expectedType; } } break; // optimize ceq/cne (int/long-to-enum(int), yyy) case AstCode.Ceq: case AstCode.Cne: { if (node.Arguments.Any(a => IsToEnum(a.Code))) { // xx_to_enum is a quite costly operation when compared to enum-to-int, // so convert this to an interger-only compare. bool isLong = node.Arguments.Any(a => a.Code == AstCode.Long_to_enum); foreach (var arg in node.Arguments) { if (IsToEnum(arg.Code)) { arg.CopyFrom(arg.Arguments[0]); arg.ExpectedType = isLong ? compiler.Module.TypeSystem.Long : compiler.Module.TypeSystem.Int; } else { Debug.Assert(arg.GetResultType().IsEnum()); var orig = new AstExpression(arg); var convert = new AstExpression(arg.SourceLocation, isLong ? AstCode.Enum_to_long : AstCode.Enum_to_int, null, orig); convert.ExpectedType = isLong ? compiler.Module.TypeSystem.Long : compiler.Module.TypeSystem.Int; arg.CopyFrom(convert); } } } break; } } } }