From 3a94d8147fd15ea6920cadf078e164cb238250b3 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 12 May 2014 13:03:27 +0200 Subject: [PATCH 1/2] [Rewrite] Fixed 2d/3d arrays on .Net Mono can use ldlen and ldelema on both 1d arrays (vectors) and 2d/3d arrays. However, .Net can only use these instructions on 1d arrays - higher rank arrays must use get_Length and Address explicitly. --- Source/Generator.Rewrite/Program.cs | 66 ++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/Source/Generator.Rewrite/Program.cs b/Source/Generator.Rewrite/Program.cs index b7254f8f..170fd9dc 100644 --- a/Source/Generator.Rewrite/Program.cs +++ b/Source/Generator.Rewrite/Program.cs @@ -54,6 +54,7 @@ namespace OpenTK.Rewrite static TypeDefinition TypeStringBuilder; static TypeDefinition TypeVoid; static TypeDefinition TypeIntPtr; + static TypeDefinition TypeInt32; // OpenTK.BindingsBase static TypeDefinition TypeBindingsBase; @@ -119,6 +120,7 @@ namespace OpenTK.Rewrite TypeStringBuilder = mscorlib.MainModule.GetType("System.Text.StringBuilder"); TypeVoid = mscorlib.MainModule.GetType("System.Void"); TypeIntPtr = mscorlib.MainModule.GetType("System.IntPtr"); + TypeInt32 = mscorlib.MainModule.GetType("System.Int32"); TypeBindingsBase = assembly.Modules.Select(m => m.GetType("OpenTK.BindingsBase")).First(); @@ -747,8 +749,28 @@ namespace OpenTK.Rewrite { if (p.Name != method.Module.Import(typeof(string[])).Name) { + // .Net treats 1d arrays differently than higher rank arrays. + // 1d arrays are directly supported by instructions such as ldlen and ldelema. + // Higher rank arrays must be accessed through System.Array methods such as get_Length. + // 1d array: + // check array is not null + // check ldlen array > 0 + // ldc.i4.0 + // ldelema + // 2d array: + // check array is not null + // check array.get_Length() > 0 + // ldc.i4.0 + // ldc.i4.0 + // call instance T& T[0..., 0...]::Address(int32, int32) + // Mono treats everything as a 1d array. + // Interestingly, the .Net approach works on both Mono and .Net. + // The Mono approach fails when using high-rank arrays on .Net. + // We should report a bug to http://bugzilla.xamarin.com + // Pin the array and pass the address // of its first element. + var array = (ArrayType)p; var element_type = p.GetElementType(); body.Variables.Add(new VariableDefinition(new PinnedType(new ByReferenceType(element_type)))); int pinned_index = body.Variables.Count - 1; @@ -756,14 +778,23 @@ namespace OpenTK.Rewrite var empty = il.Create(OpCodes.Ldc_I4, 0); var pin = il.Create(OpCodes.Ldarg, i); var end = il.Create(OpCodes.Stloc, pinned_index); - + // if (array == null) goto empty il.Emit(OpCodes.Brfalse, empty); // else if (array.Length != 0) goto pin il.Emit(OpCodes.Ldarg, i); - il.Emit(OpCodes.Ldlen); - il.Emit(OpCodes.Conv_I4); + if (array.Rank == 1) + { + il.Emit(OpCodes.Ldlen); + il.Emit(OpCodes.Conv_I4); + } + else + { + var get_length = method.Module.Import( + mscorlib.MainModule.GetType("System.Array").Methods.First(m => m.Name == "get_Length")); + il.Emit(OpCodes.Callvirt, get_length); + } il.Emit(OpCodes.Brtrue, pin); // empty: IntPtr ptr = IntPtr.Zero @@ -773,8 +804,33 @@ namespace OpenTK.Rewrite // pin: &array[0] il.Append(pin); - il.Emit(OpCodes.Ldc_I4, 0); - il.Emit(OpCodes.Ldelema, element_type); + if (array.Rank == 1) + { + // 1d array (vector), address is taken by ldelema + il.Emit(OpCodes.Ldc_I4, 0); + il.Emit(OpCodes.Ldelema, element_type); + } + else + { + // 2d-3d array, address must be taken as follows: + // call instance T& T[0..., 0..., 0...]::Address(int, int, int) + ByReferenceType t_ref = array.IsGenericParameter ? + array.ElementType.MakeByReferenceType() : + array.ElementType.MakeByReferenceType(); + MethodReference get_address = new MethodReference("Address", t_ref, array); + for (int r = 0; r < array.Rank; r++) + { + get_address.Parameters.Add(new ParameterDefinition(TypeInt32)); + } + get_address.HasThis = true; + + // emit the get_address call + for (int r = 0; r < array.Rank; r++) + { + il.Emit(OpCodes.Ldc_I4, 0); + } + il.Emit(OpCodes.Call, get_address); + } // end: fixed (IntPtr ptr = &array[0]) il.Append(end); From bb01f35de946327df3d76e3ce8c91708b2f09a07 Mon Sep 17 00:00:00 2001 From: thefiddler Date: Mon, 12 May 2014 16:34:02 +0200 Subject: [PATCH 2/2] [Rewrite] Removed unused conditional. --- Source/Generator.Rewrite/Program.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/Generator.Rewrite/Program.cs b/Source/Generator.Rewrite/Program.cs index 170fd9dc..c5c3b5cb 100644 --- a/Source/Generator.Rewrite/Program.cs +++ b/Source/Generator.Rewrite/Program.cs @@ -814,9 +814,7 @@ namespace OpenTK.Rewrite { // 2d-3d array, address must be taken as follows: // call instance T& T[0..., 0..., 0...]::Address(int, int, int) - ByReferenceType t_ref = array.IsGenericParameter ? - array.ElementType.MakeByReferenceType() : - array.ElementType.MakeByReferenceType(); + ByReferenceType t_ref = array.ElementType.MakeByReferenceType(); MethodReference get_address = new MethodReference("Address", t_ref, array); for (int r = 0; r < array.Rank; r++) {