[runtime] Change jit_info_table_num_elements to O(1).
# This can be reset to 0 when Mono's version number is bumped
# since it's part of the corlib version (the prefix '1' in the full
# version number is to ensure the number isn't treated as octal in C)
-MONO_CORLIB_COUNTER=1
+MONO_CORLIB_COUNTER=2
MONO_CORLIB_VERSION=`printf "1%02d%02d%02d%03d" $MONO_VERSION_MAJOR $MONO_VERSION_MINOR $MONO_VERSION_BUILD $MONO_CORLIB_COUNTER`
AC_DEFINE_UNQUOTED(MONO_CORLIB_VERSION,$MONO_CORLIB_VERSION,[Version of the corlib-runtime interface])
-Subproject commit c783777342f4d39b58f3c2e6a1659eedf504ac87
+Subproject commit 7dba4865168e7719aacf21bec5497ae99f8a86e4
using System.Runtime.InteropServices;
using System.Security.Permissions;
-[assembly: AssemblyVersion (Consts.FxVersion)]
+
+#if MONO_POSIX_NETSTANDARD_BUILD
+[assembly: AssemblyVersion ("1.0.0.0")]
+[assembly: AssemblyTitle("Mono.Posix.NETStandard.dll")]
+#else
+[assembly: AssemblyVersion (Consts.FxVersion)]
[assembly: AssemblyTitle("Mono.Posix.dll")]
+#endif
+
[assembly: AssemblyDescription("Unix Integration Classes")]
[assembly: CLSCompliant (true)]
*/
+#if !MONO_POSIX_NETSTANDARD_BUILD
+// We are using ../Open.snk for MONO_POSIX_NETSTANDARD_BUILD
[assembly: AssemblyDelaySign (true)]
[assembly: AssemblyKeyFile ("../mono.pub")]
-
+#endif
/*
* TODO:
*
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">\r
+\r
+ <PropertyGroup>\r
+ <TargetFramework>netstandard2.0</TargetFramework>\r
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\r
+ <AssemblyName>Mono.Posix.NETStandard</AssemblyName>\r
+ <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>\r
+ <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>\r
+ <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>\r
+ <EnableDefaultItems>false</EnableDefaultItems>\r
+ <SignAssembly>true</SignAssembly>\r
+ <DelaySign>true</DelaySign>\r
+ <AssemblyOriginatorKeyFile>..\Open.snk</AssemblyOriginatorKeyFile>\r
+ <!--<BaseIntermediateOutputPath>obj-netstandard2.0</BaseIntermediateOutputPath>\r
+ <IntermediateOutputPath>obj-netstandard2.0</IntermediateOutputPath>-->\r
+ </PropertyGroup>\r
+\r
+ <PropertyGroup>\r
+ <DefineConstants>$(DefineConstants);MONO_POSIX_NETSTANDARD_BUILD</DefineConstants>\r
+ </PropertyGroup>\r
+\r
+ <PropertyGroup Condition="'$(ForceUseLibC)' == 'true'">\r
+ <DefineConstants>$(DefineConstants);FORCE_USE_LIBC_NOT_MSVC</DefineConstants>\r
+ </PropertyGroup>\r
+\r
+ <ItemGroup>\r
+ <Compile Include=".\Assembly\**\*.cs" />\r
+ <Compile Include=".\Mono.Posix\**\*.cs" />\r
+ <Compile Include=".\Mono.Unix\**\*.cs" />\r
+ <Compile Include=".\Mono.Unix.Native\**\*.cs" />\r
+ <Compile Include="..\..\build\common\Locale.cs" />\r
+ <Compile Remove=".\Mono.Unix.Native\CdeclFunction.cs" />\r
+ </ItemGroup>\r
+\r
+</Project>\r
public delegate void SignalHandler (int signal);
+#if !NETSTANDARD2_0
internal class XPrintfFunctions
{
internal delegate object XPrintf (object[] parameters);
syslog = new XPrintf (_syslog.Invoke);
}
}
+#endif
//
// Convention: Functions that are part of the C standard library go here.
//
public class Stdlib
{
+#if FORCE_USE_LIBC_NOT_MSVC
+ internal const string LIBC = "c";
+#else
internal const string LIBC = "msvcrt";
+#endif
internal const string MPH = "MonoPosixHelper";
// It is possible for Mono.Posix and MonoPosixHelper to get out of sync,
return sys_fprintf (stream, "%s", message);
}
+#if !NETSTANDARD2_0
[Obsolete ("Not necessarily portable due to cdecl restrictions.\n" +
"Use fprintf (IntPtr, string) instead.")]
public static int fprintf (IntPtr stream, string format, params object[] parameters)
Array.Copy (parameters, 0, _parameters, 2, parameters.Length);
return (int) XPrintfFunctions.fprintf (_parameters);
}
+#endif
/* SKIP: fscanf(3) */
return sys_printf ("%s", message);
}
+#if !NETSTANDARD2_0
[Obsolete ("Not necessarily portable due to cdecl restrictions.\n" +
"Use printf (string) instead.")]
public static int printf (string format, params object[] parameters)
Array.Copy (parameters, 0, _parameters, 1, parameters.Length);
return (int) XPrintfFunctions.printf (_parameters);
}
+#endif
/* SKIP: scanf(3) */
return sys_snprintf (s, (ulong) s.Capacity, "%s", message);
}
+#if !NETSTANDARD2_0
[CLSCompliant (false)]
[Obsolete ("Not necessarily portable due to cdecl restrictions.\n" +
"Use snprintf (StringBuilder, string) instead.")]
Array.Copy (parameters, 0, _parameters, 3, parameters.Length);
return (int) XPrintfFunctions.snprintf (_parameters);
}
+#endif
/*
* SKIP:
return UnixMarshal.EscapeFormatString (message, new char[]{'m'});
}
+#if !NETSTANDARD2_0
[Obsolete ("Not necessarily portable due to cdecl restrictions.\n" +
"Use syslog(SyslogFacility, SyslogLevel, string) instead.")]
public static int syslog (SyslogFacility facility, SyslogLevel level,
Array.Copy (parameters, 0, _parameters, 2, parameters.Length);
return (int) XPrintfFunctions.syslog (_parameters);
}
+#endif
[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_closelog")]
Assert.IsFalse (NativeConvert.ToSignum (st.signalReceived) == Signum.SIGURG,
"#IH: Signal Handler invoked when it should have been removed!");
}
-
+#if !NETCOREAPP2_0
[Test]
// MSVCRT.DLL doesn't export snprintf(3).
[Category ("NotDotNet")]
Assert.AreEqual (s.ToString(), expected,
"#SNPF: printf of many builtin types failed");
}
+#endif
}
}
// handle.h HandleStackMark
struct HandleStackMark {
- int size;
+ int size, interior_size;
IntPtr chunk;
}
}
}
- if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
- mode != FileMode.CreateNew && !File.Exists (path)) {
- // don't leak the path information for isolated storage
- string msg = Locale.GetText ("Could not find file \"{0}\".");
- string fname = GetSecureFileName (path);
- throw new FileNotFoundException (String.Format (msg, fname), fname);
- }
-
// IsolatedStorage needs to keep the Name property to the default "[Unknown]"
if (!anonymous)
this.name = path;
#if LIBC
private static TimeZoneInfo FindSystemTimeZoneByFileName (string id, string filepath)
{
- if (!File.Exists (filepath))
- throw new TimeZoneNotFoundException ();
-
- using (FileStream stream = File.OpenRead (filepath)) {
+ FileStream stream = null;
+ try {
+ stream = File.OpenRead (filepath);
+ } catch (Exception ex) {
+ throw new TimeZoneNotFoundException ("Couldn't read time zone file " + filepath, ex);
+ }
+ try {
return BuildFromStream (id, stream);
+ } finally {
+ if (stream != null)
+ stream.Dispose();
}
}
#endif
using System.IO;
using System.Globalization;
using System.Threading;
+using System.Runtime.InteropServices;
+
using NUnit.Framework;
MoveTest (FileAccess.ReadWrite, FileShare.Write | FileShare.Delete, true);
MoveTest (FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, true);
}
+
+
+ [DllImport ("libc", SetLastError=true)]
+ public static extern int symlink (string oldpath, string newpath);
+
+ [Test]
+ public void SymLinkLoop ()
+ {
+ if (!RunningOnUnix)
+ Assert.Ignore ("Symlink are hard on windows");
+
+ var name1 = Path.GetRandomFileName ();
+ var name2 = Path.GetRandomFileName ();
+
+ var path1 = Path.Combine (Path.GetTempPath (), name1);
+ var path2 = Path.Combine (Path.GetTempPath (), name2);
+
+ File.Delete (path1);
+ File.Delete (path2);
+
+ try {
+ symlink (path1, path2);
+ symlink (path2, path1);
+
+ Assert.IsTrue (File.Exists (path1), "File.Exists must return true for path1 symlink loop");
+ Assert.IsTrue (File.Exists (path2), "File.Exists must return true for path2 symlink loop");
+
+ try {
+ using (var f = File.Open (path1, FileMode.Open, FileAccess.Read)) {
+ Assert.Fail ("File.Open must throw for symlink loops");
+ }
+ } catch (IOException ex) {
+ Assert.AreEqual (0x80070781u, (uint)ex.HResult, "Ensure HRESULT is correct");
+ }
+
+ File.Delete (path1); //Delete must not throw and must work
+ Assert.IsFalse (File.Exists (path1), "File.Delete must delete symlink loops");
+
+ } finally {
+ try {
+ File.Delete (path1);
+ File.Delete (path2);
+ } catch (IOException) {
+ //Don't double fault any exception from the tests.
+ }
+
+ }
+ }
}
}
using System.Threading;
using System.Collections.Generic;
+#pragma warning disable CS1718
+
namespace MonoTests.System
{
[TestFixture]
{
static partial class Debug
{
- static readonly string NewLine = Environment.NewLine;
-
static void ShowAssertDialog (string stackTrace, string message, string detailMessage)
{
// FIXME should we g_error in this case?
internal override DirectoryInfo CreateObject(SearchResult result)
{
String name = result.FullPath;
- String permissionName = name + "\\.";
#if MONO_FEATURE_CAS
+ String permissionName = name + "\\.";
+
#if FEATURE_CORECLR
FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, permissionName);
state.EnsureState();
if (isDir)
{
String name = result.FullPath;
- String permissionName = name + "\\.";
#if MONO_FEATURE_CAS
+ String permissionName = name + "\\.";
+
#if FEATURE_CORECLR
FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, permissionName);
state.EnsureState();
--- /dev/null
+// CS1031: Type expected
+// Line: 16
+
+public class B<Y> where Y: B<Y>
+{
+}
+
+public class A<X>: B<A<X>>
+{
+}
+
+public class Repro
+{
+ public static void Main (string[] args)
+ {
+ var h = typeof (B<A<>>);
+ }
+}
\ No newline at end of file
else if (the_token == Token.INTERR_NULLABLE || the_token == Token.STAR)
goto again;
else if (the_token == Token.OP_GENERICS_LT) {
- if (!parse_less_than (ref genericDimension))
+ int unused = 0;
+ if (!parse_less_than (ref unused))
return false;
goto again;
} else if (the_token == Token.OPEN_BRACKET) {
bool is_value_type = type.IsStructOrEnum;
VariableReference vr = target as VariableReference;
+ bool prepare_await = ec.HasSet (BuilderContext.Options.AsyncBody) && arguments?.ContainsEmitWithAwait () == true;
+
if (target != null && is_value_type && (vr != null || method == null)) {
+ if (prepare_await) {
+ arguments = arguments.Emit (ec, false, true);
+ prepare_await = false;
+ }
+
target.AddressOf (ec, AddressOp.Store);
} else if (vr != null && vr.IsRef) {
vr.EmitLoad (ec);
}
if (arguments != null) {
- if (ec.HasSet (BuilderContext.Options.AsyncBody) && (arguments.Count > (this is NewInitialize ? 0 : 1)) && arguments.ContainsEmitWithAwait ())
+ if (prepare_await)
arguments = arguments.Emit (ec, false, true);
arguments.Emit (ec);
--- /dev/null
+using System.Threading.Tasks;
+
+public class A
+{
+ public async Task<ValueType> Test1 (int input2)
+ {
+ return new ValueType (await Task.FromResult (12345));
+ }
+
+ public static void Main ()
+ {
+ var a = new A ();
+ a.Test1 (1).Wait ();
+ }
+}
+
+public struct ValueType
+{
+ public ValueType (int field2)
+ {
+ }
+}
</method>
</type>
</test>
+ <test name="test-943.cs">
+ <type name="MyStruct">
+ <method name="Int32 get_X()" attrs="2182">
+ <size>14</size>
+ </method>
+ <method name="Void set_X(Int32)" attrs="2182">
+ <size>8</size>
+ </method>
+ </type>
+ <type name="X">
+ <method name="Int32 Main()" attrs="150">
+ <size>44</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
<test name="test-95.cs">
<type name="X">
<method name="Int32 Main()" attrs="150">
</method>
</type>
</test>
+ <test name="test-async-92.cs">
+ <type name="A">
+ <method name="System.Threading.Tasks.Task`1[ValueType] Test1(Int32)" attrs="134">
+ <size>33</size>
+ </method>
+ <method name="Void Main()" attrs="150">
+ <size>20</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="ValueType">
+ <method name="Void .ctor(Int32)" attrs="6278">
+ <size>2</size>
+ </method>
+ </type>
+ <type name="A+<Test1>c__async0">
+ <method name="Void MoveNext()" attrs="486">
+ <size>174</size>
+ </method>
+ <method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">
+ <size>13</size>
+ </method>
+ </type>
+ </test>
<test name="test-cls-00.cs">
<type name="CLSCLass_6">
<method name="Void add_Disposed(Delegate)" attrs="2182">
}
if (error != null) {
+ Console.Error.WriteLine ("Failure to load i18n assemblies, the following directories were searched for the assemblies:");
+ foreach (var path in link_paths){
+ Console.Error.WriteLine (" Path: " + path);
+ }
+ if (custom_mode){
+ Console.WriteLine ("In Custom mode, you need to provide the directory to lookup assemblies from using -L");
+ }
+
Error ("Couldn't load one or more of the i18n assemblies: " + error);
Environment.Exit (1);
}
Assembly a = null;
try {
+ if (!quiet)
+ Console.WriteLine ("Attempting to load assembly: {0}", assembly);
a = universe.LoadFile (assembly);
+ if (!quiet)
+ Console.WriteLine ("Assembly {0} loaded successfully.", assembly);
+
} catch (FileNotFoundException){
Error ($"Cannot find assembly `{assembly}'");
} catch (IKVM.Reflection.BadImageFormatException f) {
string total_log = "";
foreach (string dir in link_paths){
string full_path = Path.Combine (dir, assembly);
+ if (!quiet)
+ Console.WriteLine ("Attempting to load assembly from: " + full_path);
if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
full_path += ".dll";
metadata_lib=$(top_builddir)/mono/metadata/libmonoruntimesgen-static.la
gc_lib=$(top_builddir)/mono/sgen/libmonosgen-static.la
else
-metadata_lib=$(top_builddir)/mono/metadata/libmonoruntime-static.a
+metadata_lib=$(top_builddir)/mono/metadata/libmonoruntime-static.la
gc_lib=$(LIBGC_STATIC_LIBS)
endif
MonoAssembly*
mono_try_assembly_resolve_handle (MonoDomain *domain, MonoStringHandle fname, MonoAssembly *requesting, gboolean refonly, MonoError *error)
{
+ HANDLE_FUNCTION_ENTER ();
MonoAssembly *ret = NULL;
MonoMethod *method;
MonoBoolean isrefonly;
error_init (error);
if (mono_runtime_get_no_exec ())
- return ret;
+ goto leave;
g_assert (domain != NULL && !MONO_HANDLE_IS_NULL (fname));
MonoReflectionAssemblyHandle requesting_handle;
if (requesting) {
requesting_handle = mono_assembly_get_object_handle (domain, requesting, error);
- return_val_if_nok (error, ret);
+ if (!is_ok (error))
+ goto leave;
}
params [0] = MONO_HANDLE_RAW (fname);
params[1] = requesting ? MONO_HANDLE_RAW (requesting_handle) : NULL;
params [2] = &isrefonly;
MonoReflectionAssemblyHandle result = MONO_HANDLE_NEW (MonoReflectionAssembly, mono_runtime_invoke_checked (method, domain->domain, params, error));
ret = !MONO_HANDLE_IS_NULL (result) ? MONO_HANDLE_GETVAL (result, assembly) : NULL;
- return ret;
+leave:
+ HANDLE_FUNCTION_RETURN_VAL (ret);
}
MonoAssembly *
static void
mono_domain_fire_assembly_load (MonoAssembly *assembly, gpointer user_data)
{
+ HANDLE_FUNCTION_ENTER ();
static MonoClassField *assembly_load_field;
static MonoMethod *assembly_load_method;
MonoError error;
if (!domain->domain)
/* This can happen during startup */
- return;
+ goto leave;
#ifdef ASSEMBLY_LOAD_DEBUG
fprintf (stderr, "Loading %s into domain %s\n", assembly->aname.name, domain->friendly_name);
#endif
mono_field_get_value ((MonoObject*) domain->domain, assembly_load_field, &load_value);
if (load_value == NULL) {
/* No events waiting to be triggered */
- return;
+ goto leave;
}
MonoReflectionAssemblyHandle ref_assembly = mono_assembly_get_object_handle (domain, assembly, &error);
mono_runtime_invoke_checked (assembly_load_method, domain->domain, params, &error);
mono_error_cleanup (&error);
+leave:
+ HANDLE_FUNCTION_RETURN ();
}
/*
static MonoMethod*
mono_class_get_virtual_methods (MonoClass* klass, gpointer *iter)
{
- MonoMethod** method;
+ gboolean static_iter = FALSE;
+
if (!iter)
return NULL;
- if (klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass)) {
+
+ /*
+ * If the lowest bit of the iterator is 1, this is an iterator for static metadata,
+ * and the upper bits contain an index. Otherwise, the iterator is a pointer into
+ * klass->methods.
+ */
+ if ((gsize)(*iter) & 1)
+ static_iter = TRUE;
+ /* Use the static metadata only if klass->methods is not yet initialized */
+ if (!static_iter && !(klass->methods || !MONO_CLASS_HAS_STATIC_METADATA (klass)))
+ static_iter = TRUE;
+
+ if (!static_iter) {
+ MonoMethod** methodptr;
+
if (!*iter) {
mono_class_setup_methods (klass);
/*
if (!klass->methods)
return NULL;
/* start from the first */
- method = &klass->methods [0];
+ methodptr = &klass->methods [0];
} else {
- method = (MonoMethod **)*iter;
- method++;
+ methodptr = (MonoMethod **)*iter;
+ methodptr++;
}
+ if (*iter)
+ g_assert ((guint64)(*iter) > 0x100);
int mcount = mono_class_get_method_count (klass);
- while (method < &klass->methods [mcount]) {
- if (*method && ((*method)->flags & METHOD_ATTRIBUTE_VIRTUAL))
+ while (methodptr < &klass->methods [mcount]) {
+ if (*methodptr && ((*methodptr)->flags & METHOD_ATTRIBUTE_VIRTUAL))
break;
- method ++;
+ methodptr ++;
}
- if (method < &klass->methods [mcount]) {
- *iter = method;
- return *method;
+ if (methodptr < &klass->methods [mcount]) {
+ *iter = methodptr;
+ return *methodptr;
} else {
return NULL;
}
if (!*iter) {
start_index = 0;
} else {
- start_index = GPOINTER_TO_UINT (*iter);
+ start_index = GPOINTER_TO_UINT (*iter) >> 1;
}
int first_idx = mono_class_get_first_method_idx (klass);
mono_error_cleanup (&error); /* FIXME don't swallow the error */
/* Add 1 here so the if (*iter) check fails */
- *iter = GUINT_TO_POINTER (i + 1);
+ *iter = GUINT_TO_POINTER (((i + 1) << 1) | 1);
return res;
} else {
return NULL;
for (i = 1; i <= sig->param_count; i++)
mono_mb_emit_ldarg (mb, i);
- if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
+ if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || mono_class_is_interface (method->klass)) {
MonoMethod * native_wrapper = mono_cominterop_get_native_wrapper(method);
mono_mb_emit_managed_call (mb, native_wrapper, NULL);
}
* d.tramp_info contains additional data in this case.
*/
gboolean is_trampoline:1;
+ /* Whenever this jit info refers to an interpreter method */
+ gboolean is_interp:1;
/* FIXME: Embed this after the structure later*/
gpointer gc_info; /* Currently only used by SGen */
#define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100
-enum {
- HANDLE_CHUNK_PTR_OBJ = 0x0, /* chunk element points to beginning of a managed object */
- HANDLE_CHUNK_PTR_INTERIOR = 0x1, /* chunk element points into the middle of a managed object */
- HANDLE_CHUNK_PTR_MASK = 0x1
-};
-
-/* number of bits in each word of the interior pointer bitmap */
-#define INTERIOR_HANDLE_BITMAP_BITS_PER_WORD (sizeof(guint32) << 3)
-
-static gboolean
-bitset_bits_test (guint32 *bitmaps, int idx)
-{
- int w = idx / INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- int b = idx % INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- guint32 bitmap = bitmaps [w];
- guint32 mask = 1u << b;
- return ((bitmap & mask) != 0);
-}
-
-static void
-bitset_bits_set (guint32 *bitmaps, int idx)
-{
- int w = idx / INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- int b = idx % INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- guint32 *bitmap = &bitmaps [w];
- guint32 mask = 1u << b;
- *bitmap |= mask;
-}
-static void
-bitset_bits_clear (guint32 *bitmaps, int idx)
-{
- int w = idx / INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- int b = idx % INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- guint32 *bitmap = &bitmaps [w];
- guint32 mask = ~(1u << b);
- *bitmap &= mask;
-}
-
-static gpointer*
-chunk_element_objslot_init (HandleChunk *chunk, int idx, gboolean interior)
-{
- if (interior)
- bitset_bits_set (chunk->interior_bitmap, idx);
- else
- bitset_bits_clear (chunk->interior_bitmap, idx);
- return &chunk->elems [idx].o;
-}
-
static HandleChunkElem*
chunk_element (HandleChunk *chunk, int idx)
{
return &chunk->elems[idx];
}
-static guint
-chunk_element_kind (HandleChunk *chunk, int idx)
-{
- return bitset_bits_test (chunk->interior_bitmap, idx) ? HANDLE_CHUNK_PTR_INTERIOR : HANDLE_CHUNK_PTR_OBJ;
-}
-
static HandleChunkElem*
handle_to_chunk_element (MonoObjectHandle o)
{
}
#endif
-MonoRawHandle
-#ifndef MONO_HANDLE_TRACK_OWNER
-mono_handle_new (MonoObject *object)
-#else
-mono_handle_new (MonoObject *object, const char *owner)
-#endif
-{
-#ifndef MONO_HANDLE_TRACK_OWNER
- return mono_handle_new_full (object, FALSE);
-#else
- return mono_handle_new_full (object, FALSE, owner);
-#endif
-}
/* Actual handles implementation */
MonoRawHandle
#ifndef MONO_HANDLE_TRACK_OWNER
-mono_handle_new_full (gpointer rawptr, gboolean interior)
+mono_handle_new (MonoObject *obj)
#else
-mono_handle_new_full (gpointer rawptr, gboolean interior, const char *owner)
+mono_handle_new (MonoObject *obj, const char *owner)
#endif
{
MonoThreadInfo *info = mono_thread_info_current ();
retry:
if (G_LIKELY (top->size < OBJECTS_PER_HANDLES_CHUNK)) {
int idx = top->size;
- gpointer* objslot = chunk_element_objslot_init (top, idx, interior);
+ gpointer* objslot = &top->elems [idx].o;
/* can be interrupted anywhere here, so:
* 1. make sure the new slot is null
* 2. make the new slot scannable (increment size)
mono_memory_write_barrier ();
top->size++;
mono_memory_write_barrier ();
- *objslot = rawptr;
+ *objslot = obj;
SET_OWNER (top,idx);
SET_SP (handles, top, idx);
return objslot;
}
HandleChunk *new_chunk = g_new (HandleChunk, 1);
new_chunk->size = 0;
- memset (new_chunk->interior_bitmap, 0, INTERIOR_HANDLE_BITMAP_WORDS);
new_chunk->prev = top;
new_chunk->next = NULL;
/* make sure size == 0 before new chunk is visible */
goto retry;
}
+MonoRawHandle
+#ifndef MONO_HANDLE_TRACK_OWNER
+mono_handle_new_interior (gpointer rawptr)
+#else
+mono_handle_new_interior (gpointer rawptr, const char *owner)
+#endif
+{
+ MonoThreadInfo *info = mono_thread_info_current ();
+ HandleStack *handles = (HandleStack *)info->handle_stack;
+ HandleChunk *top = handles->interior;
+#ifdef MONO_HANDLE_TRACK_SP
+ mono_handle_chunk_leak_check (handles);
+#endif
+
+ g_assert (top);
+ /*
+ * Don't extend the chunk now, interior handles are
+ * only used for icall arguments, they shouldn't
+ * overflow.
+ */
+ g_assert (top->size < OBJECTS_PER_HANDLES_CHUNK);
+ int idx = top->size;
+ gpointer *objslot = &top->elems [idx].o;
+ *objslot = NULL;
+ mono_memory_write_barrier ();
+ top->size++;
+ mono_memory_write_barrier ();
+ *objslot = rawptr;
+ SET_OWNER (top,idx);
+ SET_SP (handles, top, idx);
+ return objslot;
+}
HandleStack*
mono_handle_stack_alloc (void)
{
- HandleStack *stack = g_new (HandleStack, 1);
- HandleChunk *chunk = g_new (HandleChunk, 1);
+ HandleStack *stack = g_new0 (HandleStack, 1);
+ HandleChunk *chunk = g_new0 (HandleChunk, 1);
+ HandleChunk *interior = g_new0 (HandleChunk, 1);
- chunk->size = 0;
- memset (chunk->interior_bitmap, 0, INTERIOR_HANDLE_BITMAP_WORDS);
- chunk->prev = chunk->next = NULL;
mono_memory_write_barrier ();
stack->top = stack->bottom = chunk;
-#ifdef MONO_HANDLE_TRACK_OWNER
+ stack->interior = interior;
+#ifdef MONO_HANDLE_TRACK_SP
stack->stackmark_sp = NULL;
#endif
return stack;
c = next;
}
g_free (c);
+ g_free (stack->interior);
g_free (stack);
}
+void
+mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain)
+{
+ /* Called by the GC while clearing out objects of the given domain from the heap. */
+ /* If there are no handles-related bugs, there is nothing to do: if a
+ * thread accessed objects from the domain it was aborted, so any
+ * threads left alive cannot have any handles that point into the
+ * unloading domain. However if there is a handle leak, the handle stack is not */
+ if (!stack)
+ return;
+ /* Root domain only unloaded when mono is shutting down, don't need to check anything */
+ if (domain == mono_get_root_domain () || mono_runtime_is_shutting_down ())
+ return;
+ HandleChunk *cur = stack->bottom;
+ HandleChunk *last = stack->top;
+ if (!cur)
+ return;
+ while (cur) {
+ for (int idx = 0; idx < cur->size; ++idx) {
+ HandleChunkElem *elem = &cur->elems[idx];
+ if (!elem->o)
+ continue;
+ g_assert (mono_object_domain (elem->o) != domain);
+ }
+ if (cur == last)
+ break;
+ cur = cur->next;
+ }
+ /* We don't examine the interior pointers here because the GC treats
+ * them conservatively and anyway we don't have enough information here to
+ * find the object's vtable.
+ */
+}
+
static void
check_handle_stack_monotonic (HandleStack *stack)
{
Note that if we're running, we know the world is stopped.
*/
- HandleChunk *cur = stack->bottom;
- HandleChunk *last = stack->top;
-
- if (!cur)
- return;
+ if (precise) {
+ HandleChunk *cur = stack->bottom;
+ HandleChunk *last = stack->top;
- while (cur) {
- /* assume that object pointers will be much more common than interior pointers.
- * scan the object pointers by iterating over the chunk elements.
- * scan the interior pointers by iterating over the bitmap bits.
- */
- if (precise) {
+ while (cur) {
for (int i = 0; i < cur->size; ++i) {
HandleChunkElem* elem = chunk_element (cur, i);
- int kind = chunk_element_kind (cur, i);
gpointer* obj_slot = &elem->o;
- if (kind == HANDLE_CHUNK_PTR_OBJ && *obj_slot != NULL)
+ if (*obj_slot != NULL)
func (obj_slot, gc_data);
}
- } else {
- int elem_idx = 0;
- for (int i = 0; i < INTERIOR_HANDLE_BITMAP_WORDS; ++i) {
- elem_idx = i * INTERIOR_HANDLE_BITMAP_BITS_PER_WORD;
- if (elem_idx >= cur->size)
- break;
- /* no interior pointers in the range */
- if (cur->interior_bitmap [i] == 0)
- continue;
- for (int j = 0; j < INTERIOR_HANDLE_BITMAP_BITS_PER_WORD && elem_idx < cur->size; ++j,++elem_idx) {
- HandleChunkElem *elem = chunk_element (cur, elem_idx);
- int kind = chunk_element_kind (cur, elem_idx);
- gpointer *ptr_slot = &elem->o;
- if (kind == HANDLE_CHUNK_PTR_INTERIOR && *ptr_slot != NULL)
- func (ptr_slot, gc_data);
- }
- }
+ if (cur == last)
+ break;
+ cur = cur->next;
+ }
+ } else {
+ HandleChunk *cur = stack->interior;
+
+ if (!cur)
+ return;
+ for (int i = 0; i < cur->size; ++i) {
+ HandleChunkElem* elem = chunk_element (cur, i);
+ gpointer* ptr_slot = &elem->o;
+ if (*ptr_slot != NULL)
+ func (ptr_slot, gc_data);
}
- if (cur == last)
- break;
- cur = cur->next;
}
}
HandleChunk *chunk = chunk_element_to_chunk_idx (stack, elem, &elem_idx);
/* gchandles cannot deal with interior pointers */
g_assert (chunk != NULL);
- g_assert (chunk_element_kind (chunk, elem_idx) != HANDLE_CHUNK_PTR_INTERIOR);
return mono_gchandle_new (MONO_HANDLE_RAW (handle), pinned);
}
G_BEGIN_DECLS
-
/*
Handle stack.
#endif
} HandleChunkElem;
-/* number of guint32's needed to store the interior pointers bitmap */
-#define INTERIOR_HANDLE_BITMAP_WORDS ((OBJECTS_PER_HANDLES_CHUNK + 31) / 32)
-
struct _HandleChunk {
int size; //number of handles
- /* bits in the range 0..size-1 of interior_bitmap are valid; rest are ignored. */
- guint32 interior_bitmap [INTERIOR_HANDLE_BITMAP_WORDS];
HandleChunk *prev, *next;
HandleChunkElem elems [OBJECTS_PER_HANDLES_CHUNK];
};
#ifdef MONO_HANDLE_TRACK_SP
gpointer stackmark_sp; // C stack pointer top when from most recent mono_stack_mark_init
#endif
+ /* Chunk for storing interior pointers. Not extended right now */
+ HandleChunk *interior;
} HandleStack;
+// Keep this in sync with RuntimeStructs.cs
typedef struct {
- int size;
+ int size, interior_size;
HandleChunk *chunk;
#ifdef MONO_HANDLE_TRACK_SP
gpointer prev_sp; // C stack pointer from prior mono_stack_mark_init
#ifndef MONO_HANDLE_TRACK_OWNER
MonoRawHandle mono_handle_new (MonoObject *object);
MonoRawHandle mono_handle_new_full (gpointer rawptr, gboolean interior);
+MonoRawHandle mono_handle_new_interior (gpointer rawptr);
#else
MonoRawHandle mono_handle_new (MonoObject *object, const char* owner);
MonoRawHandle mono_handle_new_full (gpointer rawptr, gboolean interior, const char *owner);
+MonoRawHandle mono_handle_new_interior (gpointer rawptr, const char *owner);
#endif
-
void mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise);
gboolean mono_handle_stack_is_empty (HandleStack *stack);
HandleStack* mono_handle_stack_alloc (void);
void mono_handle_stack_free (HandleStack *handlestack);
MonoRawHandle mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, MonoRawHandle value);
void mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name);
+void mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain);
#ifdef MONO_HANDLE_TRACK_SP
void mono_handle_chunk_leak_check (HandleStack *handles);
HandleStack *handles = (HandleStack *)info->handle_stack;
stackmark->size = handles->top->size;
stackmark->chunk = handles->top;
+ stackmark->interior_size = handles->interior->size;
#ifdef MONO_HANDLE_TRACK_SP
stackmark->prev_sp = handles->stackmark_sp;
handles->stackmark_sp = sptop;
old_top->size = stackmark->size;
mono_memory_write_barrier ();
handles->top = old_top;
+ handles->interior->size = stackmark->interior_size;
#ifdef MONO_HANDLE_TRACK_SP
mono_memory_write_barrier (); /* write to top before prev_sp */
handles->stackmark_sp = stackmark->prev_sp;
mono_handle_array_getref (MONO_HANDLE_CAST(MonoObject, (DEST)), (HANDLE), (IDX)); \
} while (0)
-/* Handles into the interior of objects.
- *
- * Typically when working with value types, we pass them by reference. In the case where the value type
- * is a field in a managed class, the reference will be a pointer into the middle of a managed object.
- * We need to identify such pointers in order for SGen to scan them correctly.
- */
-
-#ifndef MONO_HANDLE_TRACK_OWNER
-#define MONO_HANDLE_NEW_GET_VALPTR(HANDLE,TYPE,FIELD) (TYPE_VALUE_HANDLE_NAME(TYPE))(mono_handle_new_full (&(HANDLE)->__raw->FIELD), TRUE))
-#else
-#define MONO_HANDLE_NEW_GET_VALPTR(HANDLE,TYPE,FIELD) (TYPE_VALUE_HANDLE_NAME(TYPE))(mono_handle_new_full (&(HANDLE)->__raw->FIELD), TRUE, HANDLE_OWNER_STRINGIFY(__FILE__, __LINE__))
-#endif
-
-
#define MONO_HANDLE_ASSIGN(DESTH, SRCH) \
mono_handle_assign (MONO_HANDLE_CAST (MonoObject, (DESTH)), MONO_HANDLE_CAST(MonoObject, (SRCH)))
#endif
ICALL_TYPE(DELEGATE, "System.Delegate", DELEGATE_1)
-ICALL(DELEGATE_1, "AllocDelegateLike_internal", ves_icall_System_Delegate_AllocDelegateLike_internal)
-ICALL(DELEGATE_2, "CreateDelegate_internal", ves_icall_System_Delegate_CreateDelegate_internal)
-ICALL(DELEGATE_3, "GetVirtualMethod_internal", ves_icall_System_Delegate_GetVirtualMethod_internal)
+HANDLES(ICALL(DELEGATE_1, "AllocDelegateLike_internal", ves_icall_System_Delegate_AllocDelegateLike_internal))
+HANDLES(ICALL(DELEGATE_2, "CreateDelegate_internal", ves_icall_System_Delegate_CreateDelegate_internal))
+HANDLES(ICALL(DELEGATE_3, "GetVirtualMethod_internal", ves_icall_System_Delegate_GetVirtualMethod_internal))
ICALL_TYPE(DEBUGR, "System.Diagnostics.Debugger", DEBUGR_1)
ICALL(DEBUGR_1, "IsAttached_internal", ves_icall_System_Diagnostics_Debugger_IsAttached_internal)
ICALL(MARSHAL_44, "GetCCW", ves_icall_System_Runtime_InteropServices_Marshal_GetCCW)
ICALL(MARSHAL_8, "GetComSlotForMethodInfoInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetComSlotForMethodInfoInternal)
#endif
-ICALL(MARSHAL_9, "GetDelegateForFunctionPointerInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointerInternal)
-ICALL(MARSHAL_10, "GetFunctionPointerForDelegateInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetFunctionPointerForDelegateInternal)
+HANDLES(ICALL(MARSHAL_9, "GetDelegateForFunctionPointerInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointerInternal))
+HANDLES(ICALL(MARSHAL_10, "GetFunctionPointerForDelegateInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetFunctionPointerForDelegateInternal))
#ifndef DISABLE_COM
ICALL(MARSHAL_52, "GetHRForException_WinRT", ves_icall_System_Runtime_InteropServices_Marshal_GetHRForException_WinRT)
ICALL(MARSHAL_45, "GetIDispatchForObjectInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetIDispatchForObjectInternal)
ICALL(MARSHAL_54, "GetRawIUnknownForComObjectNoAddRef", ves_icall_System_Runtime_InteropServices_Marshal_GetRawIUnknownForComObjectNoAddRef)
ICALL(MARSHAL_48, "IsComObject", ves_icall_System_Runtime_InteropServices_Marshal_IsComObject)
#endif
-ICALL(MARSHAL_12, "OffsetOf", ves_icall_System_Runtime_InteropServices_Marshal_OffsetOf)
-ICALL(MARSHAL_13, "Prelink", ves_icall_System_Runtime_InteropServices_Marshal_Prelink)
-ICALL(MARSHAL_14, "PrelinkAll", ves_icall_System_Runtime_InteropServices_Marshal_PrelinkAll)
+HANDLES(ICALL(MARSHAL_12, "OffsetOf", ves_icall_System_Runtime_InteropServices_Marshal_OffsetOf))
+HANDLES(ICALL(MARSHAL_13, "Prelink", ves_icall_System_Runtime_InteropServices_Marshal_Prelink))
+HANDLES(ICALL(MARSHAL_14, "PrelinkAll", ves_icall_System_Runtime_InteropServices_Marshal_PrelinkAll))
HANDLES(ICALL(MARSHAL_15, "PtrToStringAnsi(intptr)", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringAnsi))
ICALL(MARSHAL_16, "PtrToStringAnsi(intptr,int)", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringAnsi_len)
ICALL(MARSHAL_17, "PtrToStringBSTR", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStringBSTR)
ICALL(MARSHAL_49, "ReleaseComObjectInternal", ves_icall_System_Runtime_InteropServices_Marshal_ReleaseComObjectInternal)
ICALL(MARSHAL_29, "ReleaseInternal", ves_icall_System_Runtime_InteropServices_Marshal_ReleaseInternal)
#endif
-ICALL(MARSHAL_30, "SizeOf", ves_icall_System_Runtime_InteropServices_Marshal_SizeOf)
+HANDLES(ICALL(MARSHAL_30, "SizeOf", ves_icall_System_Runtime_InteropServices_Marshal_SizeOf))
ICALL(MARSHAL_31, "StringToBSTR", ves_icall_System_Runtime_InteropServices_Marshal_StringToBSTR)
ICALL(MARSHAL_32, "StringToHGlobalAnsi", ves_icall_System_Runtime_InteropServices_Marshal_StringToHGlobalAnsi)
ICALL(MARSHAL_33, "StringToHGlobalUni", ves_icall_System_Runtime_InteropServices_Marshal_StringToHGlobalUni)
return mono_type_get_object_handle (domain, &pklass->byval_arg, error);
}
-ICALL_EXPORT MonoObject *
-ves_icall_System_Delegate_CreateDelegate_internal (MonoReflectionType *type, MonoObject *target,
- MonoReflectionMethod *info, MonoBoolean throwOnBindFailure)
+ICALL_EXPORT MonoObjectHandle
+ves_icall_System_Delegate_CreateDelegate_internal (MonoReflectionTypeHandle ref_type, MonoObjectHandle target,
+ MonoReflectionMethodHandle info, MonoBoolean throwOnBindFailure, MonoError *error)
{
- MonoError error;
- MonoClass *delegate_class = mono_class_from_mono_type (type->type);
- MonoObject *delegate;
+ MonoType *type = MONO_HANDLE_GETVAL (ref_type, type);
+ MonoClass *delegate_class = mono_class_from_mono_type (type);
gpointer func;
- MonoMethod *method = info->method;
+ MonoMethod *method = MONO_HANDLE_GETVAL (info, method);
MonoMethodSignature *sig = mono_method_signature(method);
- mono_class_init_checked (delegate_class, &error);
- if (mono_error_set_pending_exception (&error))
- return NULL;
+ mono_class_init_checked (delegate_class, error);
+ return_val_if_nok (error, NULL_HANDLE);
if (!(delegate_class->parent == mono_defaults.multicastdelegate_class)) {
/* FIXME improve this exception message */
- mono_error_set_execution_engine (&error, "file %s: line %d (%s): assertion failed: (%s)", __FILE__, __LINE__,
+ mono_error_set_execution_engine (error, "file %s: line %d (%s): assertion failed: (%s)", __FILE__, __LINE__,
__func__,
"delegate_class->parent == mono_defaults.multicastdelegate_class");
- mono_error_set_pending_exception (&error);
- return NULL;
+ return NULL_HANDLE;
}
if (mono_security_core_clr_enabled ()) {
- if (!mono_security_core_clr_ensure_delegate_creation (method, &error)) {
+ MonoError security_error;
+ if (!mono_security_core_clr_ensure_delegate_creation (method, &security_error)) {
if (throwOnBindFailure)
- mono_error_set_pending_exception (&error);
+ mono_error_move (error, &security_error);
else
- mono_error_cleanup (&error);
- return NULL;
+ mono_error_cleanup (&security_error);
+ return NULL_HANDLE;
}
}
if (sig->generic_param_count && method->wrapper_type == MONO_WRAPPER_NONE) {
if (!method->is_inflated) {
- mono_set_pending_exception(mono_get_exception_argument("method", " Cannot bind to the target method because its signature differs from that of the delegate type"));
- return NULL;
+ mono_error_set_argument (error, "method", " Cannot bind to the target method because its signature differs from that of the delegate type");
+ return NULL_HANDLE;
}
}
- delegate = mono_object_new_checked (mono_object_domain (type), delegate_class, &error);
- if (mono_error_set_pending_exception (&error))
- return NULL;
+ MonoObjectHandle delegate = MONO_HANDLE_NEW (MonoObject, mono_object_new_checked (MONO_HANDLE_DOMAIN (ref_type), delegate_class, error));
+ return_val_if_nok (error, NULL_HANDLE);
if (method_is_dynamic (method)) {
/* Creating a trampoline would leak memory */
- func = mono_compile_method_checked (method, &error);
- if (mono_error_set_pending_exception (&error))
- return NULL;
+ func = mono_compile_method_checked (method, error);
+ return_val_if_nok (error, NULL_HANDLE);
} else {
- if (target && method->flags & METHOD_ATTRIBUTE_VIRTUAL && method->klass != mono_object_class (target))
- method = mono_object_get_virtual_method (target, method);
- gpointer trampoline = mono_runtime_create_jump_trampoline (mono_domain_get (), method, TRUE, &error);
- if (mono_error_set_pending_exception (&error))
- return NULL;
+ if (!MONO_HANDLE_IS_NULL (target) && method->flags & METHOD_ATTRIBUTE_VIRTUAL && method->klass != mono_handle_class (target)) {
+ method = mono_object_handle_get_virtual_method (target, method, error);
+ return_val_if_nok (error, NULL_HANDLE);
+ }
+ gpointer trampoline = mono_runtime_create_jump_trampoline (mono_domain_get (), method, TRUE, error);
+ return_val_if_nok (error, NULL_HANDLE);
func = mono_create_ftnptr (mono_domain_get (), trampoline);
}
- mono_delegate_ctor_with_method (delegate, target, func, method, &error);
- if (mono_error_set_pending_exception (&error))
- return NULL;
+ mono_delegate_ctor_with_method (delegate, target, func, method, error);
+ return_val_if_nok (error, NULL_HANDLE);
return delegate;
}
-ICALL_EXPORT MonoMulticastDelegate *
-ves_icall_System_Delegate_AllocDelegateLike_internal (MonoDelegate *delegate)
+ICALL_EXPORT MonoMulticastDelegateHandle
+ves_icall_System_Delegate_AllocDelegateLike_internal (MonoDelegateHandle delegate, MonoError *error)
{
- MonoError error;
- MonoMulticastDelegate *ret;
+ error_init (error);
- g_assert (mono_class_has_parent (mono_object_class (delegate), mono_defaults.multicastdelegate_class));
+ MonoClass *klass = mono_handle_class (delegate);
+ g_assert (mono_class_has_parent (klass, mono_defaults.multicastdelegate_class));
- ret = (MonoMulticastDelegate*) mono_object_new_checked (mono_object_domain (delegate), mono_object_class (delegate), &error);
- if (mono_error_set_pending_exception (&error))
- return NULL;
+ MonoMulticastDelegateHandle ret = MONO_HANDLE_NEW (MonoMulticastDelegate, mono_object_new_checked (MONO_HANDLE_DOMAIN (delegate), klass, error));
+ return_val_if_nok (error, MONO_HANDLE_CAST (MonoMulticastDelegate, NULL_HANDLE));
- ret->delegate.invoke_impl = mono_runtime_create_delegate_trampoline (mono_object_class (delegate));
+ MONO_HANDLE_SETVAL (MONO_HANDLE_CAST (MonoDelegate, ret), invoke_impl, gpointer, mono_runtime_create_delegate_trampoline (klass));
return ret;
}
-ICALL_EXPORT MonoReflectionMethod*
-ves_icall_System_Delegate_GetVirtualMethod_internal (MonoDelegate *delegate)
+ICALL_EXPORT MonoReflectionMethodHandle
+ves_icall_System_Delegate_GetVirtualMethod_internal (MonoDelegateHandle delegate, MonoError *error)
{
- MonoReflectionMethod *ret = NULL;
- MonoError error;
- MonoMethod *m;
+ error_init (error);
- m = mono_object_get_virtual_method (delegate->target, delegate->method);
- ret = mono_method_get_object_checked (mono_domain_get (), m, m->klass, &error);
- mono_error_set_pending_exception (&error);
- return ret;
+ MonoObjectHandle delegate_target = MONO_HANDLE_NEW_GET (MonoObject, delegate, target);
+ MonoMethod *m = mono_object_handle_get_virtual_method (delegate_target, MONO_HANDLE_GETVAL (delegate, method), error);
+ return_val_if_nok (error, MONO_HANDLE_CAST (MonoReflectionMethod, NULL_HANDLE));
+ return mono_method_get_object_handle (mono_domain_get (), m, m->klass, error);
}
/* System.Buffer */
return;
mono_lookup_pinvoke_call (method, &exc_class, &exc_arg);
if (exc_class) {
- mono_error_set_exception_instance (error,
- mono_exception_from_name_msg (mono_defaults.corlib, "System", exc_class, exc_arg));
+ mono_error_set_generic_error (error, "System", exc_class, "%s", exc_arg);
return;
}
/* create the wrapper, too? */
}
ICALL_EXPORT void
-ves_icall_System_Runtime_InteropServices_Marshal_Prelink (MonoReflectionMethod *method)
+ves_icall_System_Runtime_InteropServices_Marshal_Prelink (MonoReflectionMethodHandle method, MonoError *error)
{
- MonoError error;
+ error_init (error);
- prelink_method (method->method, &error);
- mono_error_set_pending_exception (&error);
+ prelink_method (MONO_HANDLE_GETVAL (method, method), error);
}
ICALL_EXPORT void
-ves_icall_System_Runtime_InteropServices_Marshal_PrelinkAll (MonoReflectionType *type)
+ves_icall_System_Runtime_InteropServices_Marshal_PrelinkAll (MonoReflectionTypeHandle type, MonoError *error)
{
- MonoError error;
- MonoClass *klass = mono_class_from_mono_type (type->type);
+ error_init (error);
+ MonoClass *klass = mono_class_from_mono_type (MONO_HANDLE_GETVAL (type, type));
MonoMethod* m;
gpointer iter = NULL;
- mono_class_init_checked (klass, &error);
- if (mono_error_set_pending_exception (&error))
- return;
+ mono_class_init_checked (klass, error);
+ return_if_nok (error);
while ((m = mono_class_get_methods (klass, &iter))) {
- prelink_method (m, &error);
- if (mono_error_set_pending_exception (&error))
- return;
+ prelink_method (m, error);
+ return_if_nok (error);
}
}
static MonoFtnPtrEHCallback ftnptr_eh_callback = ftnptr_eh_callback_default;
static void
-delegate_hash_table_add (MonoDelegate *d);
+delegate_hash_table_add (MonoDelegateHandle d);
static void
delegate_hash_table_remove (MonoDelegate *d);
gpointer
mono_delegate_to_ftnptr (MonoDelegate *delegate);
+gpointer
+mono_delegate_handle_to_ftnptr (MonoDelegateHandle delegate, MonoError *error);
+
MonoDelegate*
mono_ftnptr_to_delegate (MonoClass *klass, gpointer ftn);
+MonoDelegateHandle
+mono_ftnptr_to_delegate_handle (MonoClass *klass, gpointer ftn, MonoError *error);
+
gpointer
mono_array_to_savearray (MonoArray *array);
/* This is a JIT icall, it sets the pending exception and return NULL on error */
gpointer
-mono_delegate_to_ftnptr (MonoDelegate *delegate)
+mono_delegate_to_ftnptr (MonoDelegate *delegate_raw)
{
+ HANDLE_FUNCTION_ENTER ();
MonoError error;
+ MONO_HANDLE_DCL (MonoDelegate, delegate);
+ gpointer result = mono_delegate_handle_to_ftnptr (delegate, &error);
+ mono_error_set_pending_exception (&error);
+ HANDLE_FUNCTION_RETURN_VAL (result);
+}
+
+gpointer
+mono_delegate_handle_to_ftnptr (MonoDelegateHandle delegate, MonoError *error)
+{
+ HANDLE_FUNCTION_ENTER ();
+ gpointer result = NULL;
+ error_init (error);
MonoMethod *method, *wrapper;
MonoClass *klass;
uint32_t target_handle = 0;
- if (!delegate)
- return NULL;
+ if (MONO_HANDLE_IS_NULL (delegate))
+ goto leave;
- if (delegate->delegate_trampoline)
- return delegate->delegate_trampoline;
+ if (MONO_HANDLE_GETVAL (delegate, delegate_trampoline)) {
+ result = MONO_HANDLE_GETVAL (delegate, delegate_trampoline);
+ goto leave;
+ }
- klass = ((MonoObject *)delegate)->vtable->klass;
+ klass = mono_handle_class (delegate);
g_assert (klass->delegate);
- method = delegate->method;
- if (delegate->method_is_virtual)
- method = mono_object_get_virtual_method (delegate->target, method);
+ method = MONO_HANDLE_GETVAL (delegate, method);
+ if (MONO_HANDLE_GETVAL (delegate, method_is_virtual)) {
+ MonoObjectHandle delegate_target = MONO_HANDLE_NEW_GET (MonoObject, delegate, target);
+ method = mono_object_handle_get_virtual_method (delegate_target, method, error);
+ if (!is_ok (error))
+ goto leave;
+ }
if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) {
const char *exc_class, *exc_arg;
ftnptr = mono_lookup_pinvoke_call (method, &exc_class, &exc_arg);
if (!ftnptr) {
g_assert (exc_class);
- mono_set_pending_exception (mono_exception_from_name_msg (mono_defaults.corlib, "System", exc_class, exc_arg));
- return NULL;
+ mono_error_set_generic_error (error, "System", exc_class, "%s", exc_arg);
+ goto leave;
}
- return ftnptr;
+ result = ftnptr;
+ goto leave;
}
- if (delegate->target) {
+ MonoObjectHandle delegate_target = MONO_HANDLE_NEW_GET (MonoObject, delegate, target);
+ if (!MONO_HANDLE_IS_NULL (delegate_target)) {
/* Produce a location which can be embedded in JITted code */
- target_handle = mono_gchandle_new_weakref (delegate->target, FALSE);
+ target_handle = mono_gchandle_new_weakref (MONO_HANDLE_RAW (delegate_target), FALSE); /* FIXME: a version of mono_gchandle_new_weakref that takes a coop handle */
}
- wrapper = mono_marshal_get_managed_wrapper (method, klass, target_handle, &error);
- if (!is_ok (&error))
- goto fail;
+ wrapper = mono_marshal_get_managed_wrapper (method, klass, target_handle, error);
+ if (!is_ok (error))
+ goto leave;
- delegate->delegate_trampoline = mono_compile_method_checked (wrapper, &error);
- if (!is_ok (&error))
- goto fail;
+ MONO_HANDLE_SETVAL (delegate, delegate_trampoline, gpointer, mono_compile_method_checked (wrapper, error));
+ if (!is_ok (error))
+ goto leave;
// Add the delegate to the delegate hash table
delegate_hash_table_add (delegate);
/* when the object is collected, collect the dynamic method, too */
- mono_object_register_finalizer ((MonoObject*)delegate);
+ mono_object_register_finalizer ((MonoObject*) MONO_HANDLE_RAW (delegate));
- return delegate->delegate_trampoline;
+ result = MONO_HANDLE_GETVAL (delegate, delegate_trampoline);
-fail:
- if (target_handle != 0)
+leave:
+ if (!is_ok (error) && target_handle != 0)
mono_gchandle_free (target_handle);
- mono_error_set_pending_exception (&error);
- return NULL;
+ HANDLE_FUNCTION_RETURN_VAL (result);
}
/*
}
static void
-delegate_hash_table_add (MonoDelegate *d)
+delegate_hash_table_add (MonoDelegateHandle d)
{
guint32 gchandle;
guint32 old_gchandle;
mono_marshal_lock ();
if (delegate_hash_table == NULL)
delegate_hash_table = delegate_hash_table_new ();
+ gpointer delegate_trampoline = MONO_HANDLE_GETVAL (d, delegate_trampoline);
if (mono_gc_is_moving ()) {
- gchandle = mono_gchandle_new_weakref ((MonoObject*)d, FALSE);
- old_gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (delegate_hash_table, d->delegate_trampoline));
- g_hash_table_insert (delegate_hash_table, d->delegate_trampoline, GUINT_TO_POINTER (gchandle));
+ gchandle = mono_gchandle_new_weakref ((MonoObject*) MONO_HANDLE_RAW (d), FALSE);
+ old_gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (delegate_hash_table, delegate_trampoline));
+ g_hash_table_insert (delegate_hash_table, delegate_trampoline, GUINT_TO_POINTER (gchandle));
if (old_gchandle)
mono_gchandle_free (old_gchandle);
} else {
- g_hash_table_insert (delegate_hash_table, d->delegate_trampoline, d);
+ g_hash_table_insert (delegate_hash_table, delegate_trampoline, MONO_HANDLE_RAW (d));
}
mono_marshal_unlock ();
}
MonoDelegate*
mono_ftnptr_to_delegate (MonoClass *klass, gpointer ftn)
{
+ HANDLE_FUNCTION_ENTER ();
MonoError error;
+ MonoDelegateHandle result = mono_ftnptr_to_delegate_handle (klass, ftn, &error);
+ mono_error_set_pending_exception (&error);
+ HANDLE_FUNCTION_RETURN_OBJ (result);
+}
+
+MonoDelegateHandle
+mono_ftnptr_to_delegate_handle (MonoClass *klass, gpointer ftn, MonoError *error)
+{
+ HANDLE_FUNCTION_ENTER ();
+ error_init (error);
guint32 gchandle;
- MonoDelegate *d;
+ MonoDelegateHandle d = MONO_HANDLE_NEW (MonoDelegate, NULL);
if (ftn == NULL)
- return NULL;
+ goto leave;
mono_marshal_lock ();
if (delegate_hash_table == NULL)
gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (delegate_hash_table, ftn));
mono_marshal_unlock ();
if (gchandle)
- d = (MonoDelegate*)mono_gchandle_get_target (gchandle);
- else
- d = NULL;
+ MONO_HANDLE_ASSIGN (d, MONO_HANDLE_CAST (MonoDelegate, mono_gchandle_get_target_handle (gchandle)));
} else {
- d = (MonoDelegate *)g_hash_table_lookup (delegate_hash_table, ftn);
+ MONO_HANDLE_ASSIGN (d, MONO_HANDLE_NEW (MonoDelegate, g_hash_table_lookup (delegate_hash_table, ftn)));
mono_marshal_unlock ();
}
- if (d == NULL) {
+ if (MONO_HANDLE_IS_NULL (d)) {
/* This is a native function, so construct a delegate for it */
MonoMethodSignature *sig;
MonoMethod *wrapper;
MonoMarshalSpec **mspecs;
MonoMethod *invoke = mono_get_delegate_invoke (klass);
MonoMethodPInvoke piinfo;
- MonoObject *this_obj;
+ MonoObjectHandle this_obj;
int i;
if (use_aot_wrappers) {
wrapper = mono_marshal_get_native_func_wrapper_aot (klass);
- this_obj = mono_value_box_checked (mono_domain_get (), mono_defaults.int_class, &ftn, &error);
- if (!is_ok (&error)) {
- mono_error_set_pending_exception (&error);
- return NULL;
- }
+ this_obj = MONO_HANDLE_NEW (MonoObject, mono_value_box_checked (mono_domain_get (), mono_defaults.int_class, &ftn, error));
+ if (!is_ok (error))
+ goto leave;
} else {
memset (&piinfo, 0, sizeof (piinfo));
parse_unmanaged_function_pointer_attr (klass, &piinfo);
sig->hasthis = 0;
wrapper = mono_marshal_get_native_func_wrapper (klass->image, sig, &piinfo, mspecs, ftn);
- this_obj = NULL;
+ this_obj = MONO_HANDLE_NEW (MonoObject, NULL);
for (i = mono_method_signature (invoke)->param_count; i >= 0; i--)
if (mspecs [i])
g_free (sig);
}
- d = (MonoDelegate*)mono_object_new_checked (mono_domain_get (), klass, &error);
- if (!mono_error_ok (&error)) {
- mono_error_set_pending_exception (&error);
- return NULL;
- }
- gpointer compiled_ptr = mono_compile_method_checked (wrapper, &error);
- if (mono_error_set_pending_exception (&error))
- return NULL;
- mono_delegate_ctor_with_method ((MonoObject*)d, this_obj, compiled_ptr, wrapper, &error);
- if (mono_error_set_pending_exception (&error))
- return NULL;
- }
+ MONO_HANDLE_ASSIGN (d, MONO_HANDLE_NEW (MonoDelegate, mono_object_new_checked (mono_domain_get (), klass, error)));
+ if (!is_ok (error))
+ goto leave;
+ gpointer compiled_ptr = mono_compile_method_checked (wrapper, error);
+ if (!is_ok (error))
+ goto leave;
- if (d->object.vtable->domain != mono_domain_get ()) {
- mono_set_pending_exception (mono_get_exception_not_supported ("Delegates cannot be marshalled from native code into a domain other than their home domain"));
- return NULL;
+ mono_delegate_ctor_with_method (MONO_HANDLE_CAST (MonoObject, d), this_obj, compiled_ptr, wrapper, error);
+ if (!is_ok (error))
+ goto leave;
}
- return d;
+ g_assert (!MONO_HANDLE_IS_NULL (d));
+ if (MONO_HANDLE_DOMAIN (d) != mono_domain_get ())
+ mono_error_set_not_supported (error, "Delegates cannot be marshalled from native code into a domain other than their home domain");
+
+leave:
+ HANDLE_FUNCTION_RETURN_REF (MonoDelegate, d);
}
void
}
guint32
-ves_icall_System_Runtime_InteropServices_Marshal_SizeOf (MonoReflectionType *rtype)
+ves_icall_System_Runtime_InteropServices_Marshal_SizeOf (MonoReflectionTypeHandle rtype, MonoError *error)
{
MonoClass *klass;
MonoType *type;
guint32 layout;
- MONO_CHECK_ARG_NULL (rtype, 0);
+ error_init (error);
+
+ if (MONO_HANDLE_IS_NULL (rtype)) {
+ mono_error_set_argument_null (error, "type", "");
+ return 0;
+ }
- type = rtype->type;
+ type = MONO_HANDLE_GETVAL (rtype, type);
klass = mono_class_from_mono_type (type);
if (!mono_class_init (klass)) {
- mono_set_pending_exception (mono_class_get_exception_for_failure (klass));
+ mono_error_set_for_class_failure (error, klass);
return 0;
}
if (type->type == MONO_TYPE_PTR || type->type == MONO_TYPE_FNPTR) {
return sizeof (gpointer);
} else if (layout == TYPE_ATTRIBUTE_AUTO_LAYOUT) {
- gchar *msg;
- MonoException *exc;
-
- msg = g_strdup_printf ("Type %s cannot be marshaled as an unmanaged structure.", klass->name);
- exc = mono_get_exception_argument ("t", msg);
- g_free (msg);
- mono_set_pending_exception (exc);
+ mono_error_set_argument (error, "t", "Type %s cannot be marshaled as an unmanaged structure.", klass->name);
return 0;
}
}
int
-ves_icall_System_Runtime_InteropServices_Marshal_OffsetOf (MonoReflectionType *type, MonoString *field_name)
+ves_icall_System_Runtime_InteropServices_Marshal_OffsetOf (MonoReflectionTypeHandle ref_type, MonoStringHandle field_name, MonoError *error)
{
- MonoError error;
- MonoMarshalType *info;
- MonoClass *klass;
- char *fname;
- int match_index = -1;
-
- MONO_CHECK_ARG_NULL (type, 0);
- MONO_CHECK_ARG_NULL (field_name, 0);
-
- fname = mono_string_to_utf8_checked (field_name, &error);
- if (mono_error_set_pending_exception (&error))
+ error_init (error);
+ if (MONO_HANDLE_IS_NULL (ref_type)) {
+ mono_error_set_argument_null (error, "type", "");
return 0;
- klass = mono_class_from_mono_type (type->type);
+ }
+ if (MONO_HANDLE_IS_NULL (field_name)) {
+ mono_error_set_argument_null (error, "fieldName", "");
+ return 0;
+ }
+
+ char *fname = mono_string_handle_to_utf8 (field_name, error);
+ return_val_if_nok (error, 0);
+
+ MonoType *type = MONO_HANDLE_GETVAL (ref_type, type);
+ MonoClass *klass = mono_class_from_mono_type (type);
if (!mono_class_init (klass)) {
- mono_set_pending_exception (mono_class_get_exception_for_failure (klass));
+ mono_error_set_for_class_failure (error, klass);
return 0;
}
+ int match_index = -1;
while (klass && match_index == -1) {
MonoClassField* field;
int i = 0;
g_free (fname);
if(match_index == -1) {
- MonoException* exc;
- gchar *tmp;
-
/* Get back original class instance */
- klass = mono_class_from_mono_type (type->type);
+ klass = mono_class_from_mono_type (type);
- tmp = g_strdup_printf ("Field passed in is not a marshaled member of the type %s", klass->name);
- exc = mono_get_exception_argument ("fieldName", tmp);
- g_free (tmp);
-
- mono_set_pending_exception ((MonoException*)exc);
+ mono_error_set_argument (error, "fieldName", "Field passed in is not a marshaled member of the type %s", klass->name);
return 0;
}
- info = mono_marshal_load_type_info (klass);
+ MonoMarshalType *info = mono_marshal_load_type_info (klass);
return info->fields [match_index].offset;
}
return mono_array_addr_with_size_fast (arrayobj, mono_array_element_size (arrayobj->obj.vtable->klass), index);
}
-MonoDelegate*
-ves_icall_System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointerInternal (void *ftn, MonoReflectionType *type)
+MonoDelegateHandle
+ves_icall_System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointerInternal (void *ftn, MonoReflectionTypeHandle type, MonoError *error)
{
- MonoClass *klass = mono_type_get_class (type->type);
+ error_init (error);
+ MonoClass *klass = mono_type_get_class (MONO_HANDLE_GETVAL (type, type));
if (!mono_class_init (klass)) {
- mono_set_pending_exception (mono_class_get_exception_for_failure (klass));
+ mono_error_set_for_class_failure (error, klass);
return NULL;
}
- return mono_ftnptr_to_delegate (klass, ftn);
+ return mono_ftnptr_to_delegate_handle (klass, ftn, error);
}
gpointer
-ves_icall_System_Runtime_InteropServices_Marshal_GetFunctionPointerForDelegateInternal (MonoDelegate *delegate)
+ves_icall_System_Runtime_InteropServices_Marshal_GetFunctionPointerForDelegateInternal (MonoDelegateHandle delegate, MonoError *error)
{
- return mono_delegate_to_ftnptr (delegate);
+ error_init (error);
+ return mono_delegate_handle_to_ftnptr (delegate, error);
}
/**
mono_icall_handle_new (gpointer rawobj)
{
#ifdef MONO_HANDLE_TRACK_OWNER
- return mono_handle_new_full (rawobj, FALSE, "<marshal args>");
+ return mono_handle_new (rawobj, "<marshal args>");
#else
- return mono_handle_new_full (rawobj, FALSE);
+ return mono_handle_new (rawobj);
#endif
}
mono_icall_handle_new_interior (gpointer rawobj)
{
#ifdef MONO_HANDLE_TRACK_OWNER
- return mono_handle_new_full (rawobj, TRUE, "<marshal args>");
+ return mono_handle_new_interior (rawobj, "<marshal args>");
#else
- return mono_handle_new_full (rawobj, TRUE);
+ return mono_handle_new_interior (rawobj);
#endif
}
ves_icall_System_Runtime_InteropServices_Marshal_GetLastWin32Error (void);
guint32
-ves_icall_System_Runtime_InteropServices_Marshal_SizeOf (MonoReflectionType *rtype);
+ves_icall_System_Runtime_InteropServices_Marshal_SizeOf (MonoReflectionTypeHandle rtype, MonoError *error);
void
ves_icall_System_Runtime_InteropServices_Marshal_StructureToPtr (MonoObject *obj, gpointer dst, MonoBoolean delete_old);
ves_icall_System_Runtime_InteropServices_Marshal_PtrToStructure_type (gpointer src, MonoReflectionType *type);
int
-ves_icall_System_Runtime_InteropServices_Marshal_OffsetOf (MonoReflectionType *type, MonoString *field_name);
+ves_icall_System_Runtime_InteropServices_Marshal_OffsetOf (MonoReflectionTypeHandle type, MonoStringHandle field_name, MonoError *error);
gpointer
ves_icall_System_Runtime_InteropServices_Marshal_StringToBSTR (MonoString *string);
void*
ves_icall_System_Runtime_InteropServices_Marshal_UnsafeAddrOfPinnedArrayElement (MonoArray *arrayobj, int index);
-MonoDelegate*
-ves_icall_System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointerInternal (void *ftn, MonoReflectionType *type);
+MonoDelegateHandle
+ves_icall_System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointerInternal (void *ftn, MonoReflectionTypeHandle type, MonoError *error);
gpointer
-ves_icall_System_Runtime_InteropServices_Marshal_GetFunctionPointerForDelegateInternal (MonoDelegate *delegate);
+ves_icall_System_Runtime_InteropServices_Marshal_GetFunctionPointerForDelegateInternal (MonoDelegateHandle delegate, MonoError *error);
int
ves_icall_System_Runtime_InteropServices_Marshal_AddRefInternal (gpointer pUnk);
return new_list;
}
+static inline GList*
+g_list_append_mempool (MonoMemPool *mp, GList *list, gpointer data)
+{
+ GList *new_list;
+
+ new_list = (GList *) mono_mempool_alloc0 (mp, sizeof (GList));
+ new_list->data = data;
+ new_list->prev = g_list_last (list);
+ if (new_list->prev)
+ new_list->prev->next = new_list;
+
+ return list ? list : new_list;
+}
+
char*
mono_mempool_strdup_vprintf (MonoMemPool *pool, const char *format, va_list args);
mono_method_return_message_restore (MonoMethod *method, gpointer *params, MonoArray *out_args, MonoError *error);
gboolean
-mono_delegate_ctor_with_method (MonoObject *this_obj, MonoObject *target, gpointer addr, MonoMethod *method, MonoError *error);
+mono_delegate_ctor_with_method (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoMethod *method, MonoError *error);
gboolean
-mono_delegate_ctor (MonoObject *this_obj, MonoObject *target, gpointer addr, MonoError *error);
+mono_delegate_ctor (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoError *error);
void*
mono_class_get_allocation_ftn (MonoVTable *vtable, gboolean for_box, gboolean *pass_size_in_words);
MonoBoolean method_is_virtual;
};
+/* Safely access System.Delegate from native code */
+TYPED_HANDLE_DECL (MonoDelegate);
+
typedef struct _MonoMulticastDelegate MonoMulticastDelegate;
struct _MonoMulticastDelegate {
MonoDelegate delegate;
MonoArray *delegates;
};
+/* Safely access System.MulticastDelegate from native code */
+TYPED_HANDLE_DECL (MonoMulticastDelegate);
+
struct _MonoReflectionField {
MonoObject object;
MonoClass *klass;
* On failure returns FALSE and sets \p error.
*/
gboolean
-mono_delegate_ctor_with_method (MonoObject *this_obj, MonoObject *target, gpointer addr, MonoMethod *method, MonoError *error)
+mono_delegate_ctor_with_method (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoMethod *method, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
error_init (error);
- MonoDelegate *delegate = (MonoDelegate *)this_obj;
+ MonoDelegateHandle delegate = MONO_HANDLE_CAST (MonoDelegate, this_obj);
- g_assert (this_obj);
+ g_assert (!MONO_HANDLE_IS_NULL (this_obj));
g_assert (addr);
- g_assert (mono_class_has_parent (mono_object_class (this_obj), mono_defaults.multicastdelegate_class));
+ MonoClass *klass = mono_handle_class (this_obj);
+ g_assert (mono_class_has_parent (klass, mono_defaults.multicastdelegate_class));
if (method)
- delegate->method = method;
+ MONO_HANDLE_SETVAL (delegate, method, MonoMethod*, method);
mono_stats.delegate_creations++;
#ifndef DISABLE_REMOTING
- if (target && mono_object_is_transparent_proxy (target)) {
+ if (!MONO_HANDLE_IS_NULL (target) && mono_class_is_transparent_proxy (mono_handle_class (target))) {
g_assert (method);
method = mono_marshal_get_remoting_invoke (method);
#ifdef ENABLE_INTERPRETER
//g_error ("need RuntimeMethod in method_ptr when using interpreter");
#endif
- delegate->method_ptr = mono_compile_method_checked (method, error);
+ MONO_HANDLE_SETVAL (delegate, method_ptr, gpointer, mono_compile_method_checked (method, error));
return_val_if_nok (error, FALSE);
- MONO_OBJECT_SETREF (delegate, target, target);
+ MONO_HANDLE_SET (delegate, target, target);
} else
#endif
{
- delegate->method_ptr = addr;
- MONO_OBJECT_SETREF (delegate, target, target);
+ MONO_HANDLE_SETVAL (delegate, method_ptr, gpointer, addr);
+ MONO_HANDLE_SET (delegate, target, target);
}
- delegate->invoke_impl = callbacks.create_delegate_trampoline (delegate->object.vtable->domain, delegate->object.vtable->klass);
+ MONO_HANDLE_SETVAL (delegate, invoke_impl, gpointer, callbacks.create_delegate_trampoline (MONO_HANDLE_DOMAIN (delegate), mono_handle_class (delegate)));
if (callbacks.init_delegate)
- callbacks.init_delegate (delegate);
+ callbacks.init_delegate (MONO_HANDLE_RAW (delegate)); /* FIXME: update init_delegate callback to take a MonoDelegateHandle */
return TRUE;
}
* On failure returns FALSE and sets \p error.
*/
gboolean
-mono_delegate_ctor (MonoObject *this_obj, MonoObject *target, gpointer addr, MonoError *error)
+mono_delegate_ctor (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoError *error)
{
MONO_REQ_GC_UNSAFE_MODE;
sgen_clear_nursery_fragments ();
+ FOREACH_THREAD (info) {
+ mono_handle_stack_free_domain ((HandleStack*)info->client_info.info.handle_stack, domain);
+ } FOREACH_THREAD_END
+
if (sgen_mono_xdomain_checks && domain != mono_get_root_domain ()) {
sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
case EIO: return ERROR_INVALID_HANDLE;
case EINTR: return ERROR_IO_PENDING; /* best match I could find */
case EPIPE: return ERROR_WRITE_FAULT;
- case ELOOP: return ERROR_ACCESS_DENIED; /* Empirically found by testing desktop dotnet. */
+ case ELOOP: return ERROR_CANT_RESOLVE_FILENAME;
default:
g_error ("%s: unknown error (%d) \"%s\"", __FILE__, error, g_strerror (error));
#define ERROR_FILENAME_EXCED_RANGE 206
#define ERROR_DIRECTORY 267
#define ERROR_IO_PENDING 997
+#define ERROR_CANT_RESOLVE_FILENAME 1921
#define ERROR_ENCRYPTION_FAILED 6000
#define WSAEINTR 10004
#define WSAEBADF 10009
gchar *filename;
gint retval;
gboolean ret = FALSE;
- guint32 attrs;
#if 0
struct stat statbuf;
FileShare *shareinfo;
return(FALSE);
}
- attrs = mono_w32file_get_attributes (name);
- if (attrs == INVALID_FILE_ATTRIBUTES) {
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: file attributes error", __func__);
- /* Error set by mono_w32file_get_attributes() */
- g_free (filename);
- return(FALSE);
- }
-
#if 0
/* Check to make sure sharing allows us to open the file for
* writing. See bug 323389.
}
result = _wapi_stat (utf8_name, &buf);
- if (result == -1 && errno == ENOENT) {
+ if (result == -1 && (errno == ENOENT || errno == ELOOP)) {
/* Might be a dangling symlink... */
result = _wapi_lstat (utf8_name, &buf);
}
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
+#include <sys/types.h>
#include <sys/socket.h>
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include <errno.h>
#include <fcntl.h>
-#include <sys/types.h>
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
interp/mintops.def \
interp/mintops.c \
interp/transform.c
+else
+interp_sources = \
+ interp/interp-stubs.c
endif
if ENABLE_LLVM
aot-tests.cs \
gc-test.cs \
gshared.cs \
+ unaligned.cs \
+ MemoryIntrinsics.il \
mixed.cs
if NACL_CODEGEN
devirtualization.exe \
generics.exe \
basic-simd.exe \
+ unaligned.exe \
basic-vectors.exe
if NACL_CODEGEN
generics.exe: generics.cs TestDriver.dll generics-variant-types.dll
$(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll -r:generics-variant-types.dll -r:$(CLASS)/System.Core.dll
+unaligned.exe: unaligned.cs TestDriver.dll MemoryIntrinsics.dll
+ $(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll -r:MemoryIntrinsics.dll
+
%.exe: %.cs TestDriver.dll
$(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll
generics-variant-types.dll: generics-variant-types.il
$(ILASM) -dll -output=$@ $<
+MemoryIntrinsics.dll: MemoryIntrinsics.il
+ $(ILASM) -dll -output=$@ $<
+
if NACL_CODEGEN
GENMDESC_OPTS=--nacl
else !NACL_CODEGEN
$(MAKE) fullaotcheck GSHAREDVT=1
fullaot_regtests = $(regtests) aot-tests.exe $(if $(GSHAREDVT),gshared.exe)
+fullaot_testing_deps = generics-variant-types.dll TestDriver.dll MemoryIntrinsics.dll
FULLAOT_LIBS_UNIVERSAL = \
mscorlib.dll \
System.Configuration.dll
endif
+
FULLAOT_LIBS = $(filter-out $(FULLAOT_LIBS_DISABLED),$(FULLAOT_LIBS_UNIVERSAL))
FULLAOT_TMP_DIR=$(top_builddir)/mono/mini/fullaot-tmp
# This currently only works on amd64/arm
-fullaotcheck: $(mono) $(fullaot_regtests)
+fullaotcheck: $(mono) $(fullaot_regtests) $(fullaot_testing_deps)
rm -rf $(FULLAOT_TMP_DIR)
mkdir $(FULLAOT_TMP_DIR)
$(MAKE) fullaot-libs AOT_FLAGS="full,$(MONO_FULLAOT_ADDITIONAL_ARGS)$(INVARIANT_AOT_OPTIONS)" GSHAREDVT=$(GSHAREDVT)
- cp $(regtests) $(fullaot_regtests) generics-variant-types.dll TestDriver.dll $(FULLAOT_TMP_DIR)/
- MONO_PATH=$(FULLAOT_TMP_DIR) $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) $(LLVM_AOT_RUNTIME_OPTS) $(GSHAREDVT_RUNTIME_OPTS) --aot="full,$(MONO_FULLAOT_ADDITIONAL_ARGS)$(INVARIANT_AOT_OPTIONS)" $(FULLAOT_TMP_DIR)/{generics-variant-types.dll,TestDriver.dll,*.exe} || exit 1
+ cp $(regtests) $(fullaot_regtests) $(fullaot_testing_deps) $(FULLAOT_TMP_DIR)/
+ MONO_PATH=$(FULLAOT_TMP_DIR) $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) $(LLVM_AOT_RUNTIME_OPTS) $(GSHAREDVT_RUNTIME_OPTS) --aot="full,$(MONO_FULLAOT_ADDITIONAL_ARGS)$(INVARIANT_AOT_OPTIONS)" $(FULLAOT_TMP_DIR)/{*.dll,*.exe} || exit 1
ln -s $(if $(MONO_EXECUTABLE),$(MONO_EXECUTABLE),$$PWD/mono) $(FULLAOT_TMP_DIR)/
for i in $(fullaot_regtests); do echo $$i; MONO_PATH=$(FULLAOT_TMP_DIR) $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) --full-aot $(FULLAOT_TMP_DIR)/$$i --exclude '!FULLAOT' $(ARCH_FULLAOT_EXCLUDE) || exit 1; done
rm -rf fullaot-tmp
mkdir fullaot-tmp
$(MAKE) fullaot-libs AOT_FLAGS="llvmonly,$(MONO_FULLAOT_ADDITIONAL_ARGS)$(INVARIANT_AOT_OPTIONS)"
- cp $(llvmonly_regtests) generics-variant-types.dll TestDriver.dll fullaot-tmp/
- MONO_PATH=fullaot-tmp $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) --aot=llvmonly fullaot-tmp/{generics-variant-types.dll,TestDriver.dll,*.exe} || exit 1
+ cp $(llvmonly_regtests) $(fullaot_testing_deps) fullaot-tmp/
+ MONO_PATH=fullaot-tmp $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) --aot=llvmonly fullaot-tmp/{*.dll,*.exe} || exit 1
ln -s $$PWD/mono fullaot-tmp/
for i in $(llvmonly_regtests); do echo $$i; MONO_PATH=fullaot-tmp $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) --llvmonly fullaot-tmp/$$i --exclude '!BITCODE' || exit 1; done
--- /dev/null
+.assembly extern mscorlib
+{
+ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
+ .ver 4:0:0:0
+}
+
+.assembly 'MemoryIntrinsics'
+{
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )
+ .hash algorithm 0x00008004
+ .ver 0:0:0:0
+}
+.module 'instrics-lib.dll'
+.imagebase 0x00400000
+.file alignment 0x00000200
+.stackreserve 0x00100000
+.subsystem 0x0003
+.corflags 0x00000001
+
+.namespace Mono {
+ .class public abstract auto ansi sealed beforefieldinit Intrinsics extends [mscorlib]System.Object
+ {
+ .method public hidebysig static void Cpobj<T>(void* 'to', void* from) cil managed
+ {
+ ldarg.0
+ ldarg.1
+ cpobj !!T
+ ret
+ }
+
+ .method public hidebysig static !!T Ldobj<T>(void* 'from') cil managed
+ {
+ ldarg.0
+ ldobj !!T
+ ret
+ }
+
+ .method public hidebysig static void Stobj<T>(void* 'to', !!T 'value') cil managed
+ {
+ ldarg.0
+ ldarg.1
+ stobj !!T
+ ret
+ }
+
+ .method public hidebysig static void LdobjStObjPair<T>(void* 'to', void* 'from') cil managed
+ {
+ ldarg.0
+ ldarg.1
+ ldobj !!T
+ stobj !!T
+ ret
+ }
+
+ .method public hidebysig static void Cpblk(void* 'to', void* 'from', int32 size) cil managed
+ {
+ ldarg.0
+ ldarg.1
+ ldarg.2
+ cpblk
+ ret
+ }
+
+ .method public hidebysig static void Initblk<T>(void* 'to', int32 'value', int32 'size') cil managed
+ {
+ ldarg.0
+ ldarg.1
+ ldarg.2
+ initblk
+ ret
+ }
+
+ //Unaligned intrinsics
+ .method public hidebysig static void UnalignedCpobj<T>(void* 'to', void* from) cil managed
+ {
+ ldarg.0
+ ldarg.1
+ unaligned. 1
+ cpobj !!T
+ ret
+ }
+
+ .method public hidebysig static !!T UnalignedLdobj<T>(void* 'from') cil managed
+ {
+ ldarg.0
+ unaligned. 1
+ ldobj !!T
+ ret
+ }
+
+ .method public hidebysig static void UnalignedStobj<T>(void* 'to', !!T 'value') cil managed
+ {
+ ldarg.0
+ ldarg.1
+ unaligned. 1
+ stobj !!T
+ ret
+ }
+
+ .method public hidebysig static void UnalignedLdobjStObjPair<T>(void* 'to', void* 'from') cil managed
+ {
+ ldarg.0
+ ldarg.1
+ unaligned. 1
+ ldobj !!T
+ stobj !!T
+ ret
+ }
+
+ .method public hidebysig static void UnalignedCpblk(void* 'to', void* 'from', int32 size) cil managed
+ {
+ ldarg.0
+ ldarg.1
+ ldarg.2
+ unaligned. 1
+ cpblk
+ ret
+ }
+
+ .method public hidebysig static void UnalignedInit(void* 'to', int32 'value', int32 'size') cil managed
+ {
+ ldarg.0
+ ldarg.1
+ ldarg.2
+ unaligned. 1
+ initblk
+ ret
+ }
+
+ //Unaligned ldind
+ .method public hidebysig static int16 UnalignedLdInd2(void* 'from') cil managed
+ {
+ ldarg.0
+ unaligned. 1
+ ldind.i2
+ ret
+ }
+
+ .method public hidebysig static int32 UnalignedLdInd4(void* 'from') cil managed
+ {
+ ldarg.0
+ unaligned. 1
+ ldind.i4
+ ret
+ }
+
+ .method public hidebysig static int64 UnalignedLdInd8(void* 'from') cil managed
+ {
+ ldarg.0
+ unaligned. 1
+ ldind.i8
+ ret
+ }
+
+ .method public hidebysig static float32 UnalignedLdIndR4(void* 'from') cil managed
+ {
+ ldarg.0
+ unaligned. 1
+ ldind.r4
+ ret
+ }
+
+ .method public hidebysig static float64 UnalignedLdIndR8(void* 'from') cil managed
+ {
+ ldarg.0
+ unaligned. 1
+ ldind.r8
+ ret
+ }
+
+ .method public hidebysig static native int UnalignedLdIndI(void* 'from') cil managed
+ {
+ ldarg.0
+ unaligned. 1
+ ldind.i
+ ret
+ }
+
+ }
+}
if (!method)
return NULL;
}
- full_name = mono_method_full_name (method, TRUE);
- mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT: NOT FOUND: %s.", full_name);
- g_free (full_name);
+ if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) {
+ full_name = mono_method_full_name (method, TRUE);
+ mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT: NOT FOUND: %s.", full_name);
+ g_free (full_name);
+ }
}
return NULL;
}
#include <mono/utils/mono-threads.h>
#include <mono/utils/networking.h>
#include <mono/utils/mono-proclib.h>
+#include <mono/utils/w32api.h>
#include "debugger-agent.h"
#include "mini.h"
#include "seq-points.h"
-#include <mono/utils/w32api.h>
+#include "interp/interp.h"
/*
* On iOS we can't use System.Environment.Exit () as it will do the wrong
MonoContext ctx;
MonoDebugMethodJitInfo *jit;
MonoJitInfo *ji;
+ MonoInterpFrameHandle interp_frame;
int flags;
mgreg_t *reg_locations [MONO_MAX_IREGS];
/*
* SUSPEND/RESUME
*/
+static MonoJitInfo*
+get_top_method_ji (gpointer ip, MonoDomain **domain, gpointer *out_ip)
+{
+ MonoJitInfo *ji;
+
+ if (out_ip)
+ *out_ip = ip;
+
+ ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, domain);
+ if (!ji) {
+ /* Could be an interpreter method */
+
+ MonoLMF *lmf = mono_get_lmf ();
+ MonoInterpFrameHandle *frame;
+
+ g_assert (((guint64)lmf->previous_lmf) & 2);
+ MonoLMFExt *ext = (MonoLMFExt*)lmf;
+
+ g_assert (ext->interp_exit);
+ frame = ext->interp_exit_data;
+ ji = mono_interp_frame_get_jit_info (frame);
+ if (domain)
+ *domain = mono_domain_get ();
+ if (out_ip)
+ *out_ip = mono_interp_frame_get_ip (frame);
+ }
+ return ji;
+}
+
/*
* save_thread_context:
*
* suspended when it returns to managed code, so the parent's ctx should
* remain valid.
*/
+ MonoThreadUnwindState *state = mono_thread_info_get_suspend_state (info);
+
data.last_frame_set = FALSE;
- mono_get_eh_callbacks ()->mono_walk_stack_with_state (get_last_frame, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &data);
+ mono_get_eh_callbacks ()->mono_walk_stack_with_state (get_last_frame, state, MONO_UNWIND_SIGNAL_SAFE, &data);
if (data.last_frame_set) {
gpointer jit_tls = ((MonoThreadInfo*)tls->thread->thread_info)->jit_data;
memcpy (&tls->async_last_frame, &data.last_frame, sizeof (StackFrameInfo));
+ if (data.last_frame.type == FRAME_TYPE_INTERP_TO_MANAGED) {
+ /*
+ * Store the current lmf instead of the parent one, since that
+ * contains the interp exit data.
+ */
+ data.lmf = state->unwind_data [MONO_UNWIND_DATA_LMF];
+ }
+
copy_unwind_state_from_frame_data (&tls->async_state, &data, jit_tls);
copy_unwind_state_from_frame_data (&tls->context, &data, jit_tls);
} else {
return;
}
- ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
-
+ ji = get_top_method_ji (ip, NULL, NULL);
+ g_assert (ji);
/* Can't suspend in these methods */
method = jinfo_get_method (ji);
if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy")))
SeqPoint sp;
int flags = 0;
- if (info->type != FRAME_TYPE_MANAGED) {
+ if (info->type != FRAME_TYPE_MANAGED && info->type != FRAME_TYPE_INTERP) {
if (info->type == FRAME_TYPE_DEBUGGER_INVOKE) {
/* Mark the last frame as an invoke frame */
if (ud->frames)
frame->native_offset = info->native_offset;
frame->flags = flags;
frame->ji = info->ji;
+ frame->interp_frame = info->interp_frame;
if (info->reg_locations)
memcpy (frame->reg_locations, info->reg_locations, MONO_MAX_IREGS * sizeof (mgreg_t*));
if (ctx) {
send_type_load (method->klass);
- if (!result)
+ if (!result && jinfo)
add_pending_breakpoints (method, jinfo);
}
if (it.seq_point.native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
DEBUG_PRINTF (1, "[dbg] Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset);
} else if (count == 0) {
+ if (ji->is_interp) {
+ mono_interp_set_breakpoint (ji, inst->ip);
+ } else {
#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
- mono_arch_set_breakpoint (ji, inst->ip);
+ mono_arch_set_breakpoint (ji, inst->ip);
#else
- NOT_IMPLEMENTED;
+ NOT_IMPLEMENTED;
#endif
+ }
}
DEBUG_PRINTF (1, "[dbg] Inserted breakpoint at %s:[il=0x%x,native=0x%x] [%p](%d).\n", mono_method_full_name (jinfo_get_method (ji), TRUE), (int)it.seq_point.il_offset, (int)it.seq_point.native_offset, inst->ip, count);
g_assert (count > 0);
if (count == 1 && inst->native_offset != SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
- mono_arch_clear_breakpoint (ji, ip);
+ if (ji->is_interp)
+ mono_interp_clear_breakpoint (ji, ip);
+ else
+ mono_arch_clear_breakpoint (ji, ip);
DEBUG_PRINTF (1, "[dbg] Clear breakpoint at %s [%p].\n", mono_method_full_name (jinfo_get_method (ji), TRUE), ip);
}
#else
/* Might be AOTed code */
mono_class_init (method->klass);
code = mono_aot_get_method_checked (domain, method, &oerror);
- g_assert (code);
- mono_error_assert_ok (&oerror);
- ji = mono_jit_info_table_find (domain, (char *)code);
+ if (code) {
+ mono_error_assert_ok (&oerror);
+ ji = mono_jit_info_table_find (domain, (char *)code);
+ } else {
+ /* Might be interpreted */
+ ji = mono_interp_find_jit_info (domain, method);
+ }
g_assert (ji);
}
- g_assert (code);
insert_breakpoint (seq_points, domain, ji, bp, error);
}
}
}
- MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
- if (asyncMethod) {
- for (int i = 0; i < asyncMethod->num_awaits; i++)
- {
- if (asyncMethod->yield_offsets[i] == sp->il_offset || asyncMethod->resume_offsets[i] == sp->il_offset) {
- mono_debug_free_method_async_debug_info (asyncMethod);
+ MonoDebugMethodAsyncInfo* async_method = mono_debug_lookup_method_async_debug_info (method);
+ if (async_method) {
+ for (int i = 0; i < async_method->num_awaits; i++) {
+ if (async_method->yield_offsets[i] == sp->il_offset || async_method->resume_offsets[i] == sp->il_offset) {
+ mono_debug_free_method_async_debug_info (async_method);
return FALSE;
}
}
- mono_debug_free_method_async_debug_info (asyncMethod);
+ mono_debug_free_method_async_debug_info (async_method);
}
if (req->size != STEP_SIZE_LINE)
}
static void
-process_breakpoint_inner (DebuggerTlsData *tls, gboolean from_signal)
+process_breakpoint (DebuggerTlsData *tls, gboolean from_signal)
{
MonoJitInfo *ji;
guint8 *ip;
ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx);
ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
+
+ if (!ji) {
+ /* Interpreter */
+ // FIXME: Pass a flag instead to detect this
+ MonoLMF *lmf = mono_get_lmf ();
+ MonoInterpFrameHandle *frame;
+
+ g_assert (((guint64)lmf->previous_lmf) & 2);
+ MonoLMFExt *ext = (MonoLMFExt*)lmf;
+
+ g_assert (ext->interp_exit);
+ frame = ext->interp_exit_data;
+ ji = mono_interp_frame_get_jit_info (frame);
+ ip = mono_interp_frame_get_ip (frame);
+ }
+
g_assert (ji && !ji->is_trampoline);
method = jinfo_get_method (ji);
/* Compute the native offset of the breakpoint from the ip */
- native_offset = ip - (guint8*)ji->code_start;
+ native_offset = ip - (guint8*)ji->code_start;
/*
* Skip the instruction causing the breakpoint signal.
}
static void
-process_breakpoint (void)
+process_breakpoint_from_signal (void)
{
- process_signal_event (process_breakpoint_inner);
+ process_signal_event (process_breakpoint);
}
static void
* problems, like the original signal is disabled, libgc can't handle altstack, etc.
* So set up the signal context to return to the real breakpoint handler function.
*/
- resume_from_signal_handler (sigctx, process_breakpoint);
+ resume_from_signal_handler (sigctx, process_breakpoint_from_signal);
}
+typedef struct {
+ gboolean found;
+ MonoContext *ctx;
+} UserBreakCbData;
+
static gboolean
-user_break_cb (StackFrameInfo *frame, MonoContext *ctx, gpointer data)
+user_break_cb (StackFrameInfo *frame, MonoContext *ctx, gpointer user_data)
{
+ UserBreakCbData *data = user_data;
+
+ if (frame->type == FRAME_TYPE_INTERP_TO_MANAGED) {
+ data->found = TRUE;
+ return TRUE;
+ }
if (frame->managed) {
- *(MonoContext*)data = *ctx;
+ data->found = TRUE;
+ *data->ctx = *ctx;
return TRUE;
- } else {
- return FALSE;
}
+ return FALSE;
}
/*
MonoContext ctx;
int suspend_policy;
GSList *events;
+ UserBreakCbData data;
+
+ memset (&data, 0, sizeof (UserBreakCbData));
+ data.ctx = &ctx;
/* Obtain a context */
MONO_CONTEXT_SET_IP (&ctx, NULL);
- mono_walk_stack_with_ctx (user_break_cb, NULL, (MonoUnwindOptions)0, &ctx);
- g_assert (MONO_CONTEXT_GET_IP (&ctx) != NULL);
+ mono_walk_stack_with_ctx (user_break_cb, NULL, (MonoUnwindOptions)0, &data);
+ g_assert (data.found);
mono_loader_lock ();
events = create_event_list (EVENT_KIND_USER_BREAK, NULL, NULL, NULL, &suspend_policy);
SeqPoint sp;
MonoSeqPointInfo *info;
- ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx);
-
/* Skip the instruction causing the single step */
if (from_signal)
mono_arch_skip_single_step (ctx);
if (mono_thread_internal_current () != ss_req->thread)
return;
- if (log_level > 0) {
- ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
+ ip = (guint8 *)MONO_CONTEXT_GET_IP (ctx);
+
+ ji = get_top_method_ji (ip, &domain, (gpointer*)&ip);
+ g_assert (ji && !ji->is_trampoline);
+ if (log_level > 0) {
DEBUG_PRINTF (1, "[%p] Single step event (depth=%s) at %s (%p)[0x%x], sp %p, last sp %p\n", (gpointer) (gsize) mono_native_thread_id_get (), ss_depth_to_string (ss_req->depth), mono_method_full_name (jinfo_get_method (ji), TRUE), MONO_CONTEXT_GET_IP (ctx), (int)((guint8*)MONO_CONTEXT_GET_IP (ctx) - (guint8*)ji->code_start), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp);
}
- ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
- g_assert (ji && !ji->is_trampoline);
method = jinfo_get_method (ji);
g_assert (method);
* The ip points to the instruction causing the single step event, which is before
* the offset recorded in the seq point map, so find the next seq point after ip.
*/
- if (!mono_find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info, &sp))
+ if (!mono_find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info, &sp)) {
+ g_assert_not_reached ();
return;
+ }
il_offset = sp.il_offset;
mono_thread_state_init_from_monoctx (&tls->restore_state, ctx);
memcpy (&tls->handler_ctx, ctx, sizeof (MonoContext));
- process_breakpoint_inner (tls, FALSE);
+ process_breakpoint (tls, FALSE);
memcpy (ctx, &tls->restore_state.ctx, sizeof (MonoContext));
memcpy (&tls->restore_state, &orig_restore_state, sizeof (MonoThreadUnwindState));
#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
int val = InterlockedIncrement (&ss_count);
- if (val == 1)
+ if (val == 1) {
mono_arch_start_single_stepping ();
+ mono_interp_start_single_stepping ();
+ }
#else
g_assert_not_reached ();
#endif
#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
int val = InterlockedDecrement (&ss_count);
- if (val == 0)
+ if (val == 0) {
mono_arch_stop_single_stepping ();
+ mono_interp_stop_single_stepping ();
+ }
#else
g_assert_not_reached ();
#endif
}
}
+static void
+set_interp_var (MonoType *t, gpointer addr, guint8 *val_buf)
+{
+ int size;
+
+ if (t->byref) {
+ addr = *(gpointer*)addr;
+ g_assert (addr);
+ }
+
+ if (MONO_TYPE_IS_REFERENCE (t))
+ size = sizeof (gpointer);
+ else
+ size = mono_class_value_size (mono_class_from_mono_type (t), NULL);
+
+ memcpy (addr, val_buf, size);
+}
+
static void
clear_event_request (int req_id, int etype)
{
// FIXME: Check that the ip change is safe
DEBUG_PRINTF (1, "[dbg] Setting IP to %s:0x%0x(0x%0x)\n", tls->frames [0]->actual_method->name, (int)sp.il_offset, (int)sp.native_offset);
- MONO_CONTEXT_SET_IP (&tls->restore_state.ctx, (guint8*)tls->frames [0]->ji->code_start + sp.native_offset);
+
+ if (tls->frames [0]->ji->is_interp) {
+ MonoJitTlsData *jit_data = ((MonoThreadInfo*)thread->thread_info)->jit_data;
+ mono_interp_set_resume_state (jit_data, NULL, tls->frames [0]->interp_frame, (guint8*)tls->frames [0]->ji->code_start + sp.native_offset);
+ } else {
+ MONO_CONTEXT_SET_IP (&tls->restore_state.ctx, (guint8*)tls->frames [0]->ji->code_start + sp.native_offset);
+ }
break;
}
default:
sig = mono_method_signature (frame->actual_method);
- if (!jit->has_var_info || !mono_get_seq_points (frame->domain, frame->actual_method))
+ if (!(jit->has_var_info || frame->ji->is_interp) || !mono_get_seq_points (frame->domain, frame->actual_method))
/*
* The method is probably from an aot image compiled without soft-debug, variables might be dead, etc.
*/
DEBUG_PRINTF (4, "[dbg] send arg %d.\n", pos);
- g_assert (pos >= 0 && pos < jit->num_params);
+ if (frame->ji->is_interp) {
+ guint8 *addr;
+
+ addr = mono_interp_frame_get_arg (frame->interp_frame, pos);
+
+ buffer_add_value_full (buf, sig->params [pos], addr, frame->domain, FALSE, NULL);
+ } else {
+ g_assert (pos >= 0 && pos < jit->num_params);
- add_var (buf, jit, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE);
+ add_var (buf, jit, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE);
+ }
} else {
MonoDebugLocalsInfo *locals;
pos = locals->locals [pos].index;
mono_debug_free_locals (locals);
}
- g_assert (pos >= 0 && pos < jit->num_locals);
DEBUG_PRINTF (4, "[dbg] send local %d.\n", pos);
- add_var (buf, jit, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE);
+ if (frame->ji->is_interp) {
+ guint8 *addr;
+
+ addr = mono_interp_frame_get_local (frame->interp_frame, pos);
+
+ buffer_add_value_full (buf, header->locals [pos], addr, frame->domain, FALSE, NULL);
+ } else {
+ g_assert (pos >= 0 && pos < jit->num_locals);
+
+ add_var (buf, jit, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE);
+ }
}
}
mono_metadata_free_mh (header);
break;
}
case CMD_STACK_FRAME_GET_THIS: {
+ if (frame->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
+ return ERR_ABSENT_INFORMATION;
if (frame->api_method->klass->valuetype) {
if (!sig->hasthis) {
MonoObject *p = NULL;
buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain);
} else {
- add_var (buf, jit, &frame->actual_method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
+ if (frame->ji->is_interp) {
+ guint8 *addr;
+
+ addr = mono_interp_frame_get_this (frame->interp_frame);
+
+ buffer_add_value_full (buf, &frame->actual_method->klass->this_arg, addr, frame->domain, FALSE, NULL);
+ } else {
+ add_var (buf, jit, &frame->actual_method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
+ }
}
} else {
if (!sig->hasthis) {
MonoObject *p = NULL;
buffer_add_value (buf, &frame->actual_method->klass->byval_arg, &p, frame->domain);
} else {
- add_var (buf, jit, &frame->api_method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
+ if (frame->ji->is_interp) {
+ guint8 *addr;
+
+ addr = mono_interp_frame_get_this (frame->interp_frame);
+
+ buffer_add_value_full (buf, &frame->api_method->klass->byval_arg, addr, frame->domain, FALSE, NULL);
+ } else {
+ add_var (buf, jit, &frame->api_method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
+ }
}
}
break;
MonoError error;
guint8 *val_buf;
MonoType *t;
- MonoDebugVarInfo *var;
+ MonoDebugVarInfo *var = NULL;
+ gboolean is_arg = FALSE;
len = decode_int (p, &p, end);
header = mono_method_get_header_checked (frame->actual_method, &error);
t = sig->params [pos];
var = &jit->params [pos];
+ is_arg = TRUE;
} else {
MonoDebugLocalsInfo *locals;
if (err != ERR_NONE)
return err;
- set_var (t, var, &frame->ctx, frame->domain, val_buf, frame->reg_locations, &tls->restore_state.ctx);
+ if (frame->ji->is_interp) {
+ guint8 *addr;
+
+ if (is_arg)
+ addr = mono_interp_frame_get_arg (frame->interp_frame, pos);
+ else
+ addr = mono_interp_frame_get_local (frame->interp_frame, pos);
+ set_interp_var (t, addr, val_buf);
+ } else {
+ set_var (t, var, &frame->ctx, frame->domain, val_buf, frame->reg_locations, &tls->restore_state.ctx);
+ }
}
mono_metadata_free_mh (header);
break;
t = &frame->actual_method->klass->byval_arg;
/* Checked by the sender */
g_assert (MONO_TYPE_ISSTRUCT (t));
- var = jit->this_var;
- g_assert (var);
val_buf = (guint8 *)g_alloca (mono_class_instance_size (mono_class_from_mono_type (t)));
err = decode_value (t, frame->domain, val_buf, p, &p, end);
if (err != ERR_NONE)
return err;
- set_var (&frame->actual_method->klass->this_arg, var, &frame->ctx, frame->domain, val_buf, frame->reg_locations, &tls->restore_state.ctx);
+ if (frame->ji->is_interp) {
+ guint8 *addr;
+
+ addr = mono_interp_frame_get_this (frame->interp_frame);
+ set_interp_var (&frame->actual_method->klass->this_arg, addr, val_buf);
+ } else {
+ var = jit->this_var;
+ g_assert (var);
+
+ set_var (&frame->actual_method->klass->this_arg, var, &frame->ctx, frame->domain, val_buf, frame->reg_locations, &tls->restore_state.ctx);
+ }
break;
}
default:
EMIT_NEW_VARLOADA ((cfg), (src), src_var, src_var->inst_vtype);
EMIT_NEW_VARLOADA ((cfg), (dest), dest_var, dest_var->inst_vtype);
+ mini_emit_memory_copy (cfg, dest, src, src_var->klass, src_var->backend.is_pinvoke, 0);
- mini_emit_stobj (cfg, dest, src, src_var->klass, src_var->backend.is_pinvoke);
break;
}
case OP_VZERO:
dreg = alloc_preg (cfg);
EMIT_NEW_BIALU_IMM (cfg, dest, OP_ADD_IMM, dreg, ins->inst_destbasereg, ins->inst_offset);
- mini_emit_stobj (cfg, dest, src, src_var->klass, src_var->backend.is_pinvoke);
+ mini_emit_memory_copy (cfg, dest, src, src_var->klass, src_var->backend.is_pinvoke, 0);
break;
}
case OP_LOADV_MEMBASE: {
dreg = alloc_preg (cfg);
EMIT_NEW_BIALU_IMM (cfg, src, OP_ADD_IMM, dreg, ins->inst_basereg, ins->inst_offset);
EMIT_NEW_VARLOADA (cfg, dest, dest_var, dest_var->inst_vtype);
- mini_emit_stobj (cfg, dest, src, dest_var->klass, dest_var->backend.is_pinvoke);
+ mini_emit_memory_copy (cfg, dest, src, dest_var->klass, dest_var->backend.is_pinvoke, 0);
break;
}
case OP_OUTARG_VT: {
stackval *stack_args; /* parent */
stackval *stack;
stackval *sp; /* For GC stack marking */
+ unsigned char *locals;
/* exception info */
unsigned char invoke_trap;
const unsigned short *ip;
--- /dev/null
+#include <config.h>
+
+#ifndef ENABLE_INTERPRETER
+
+#include "interp.h"
+
+/* Dummy versions of interpreter functions to avoid ifdefs at call sites */
+
+MonoJitInfo*
+mono_interp_find_jit_info (MonoDomain *domain, MonoMethod *method)
+{
+ return NULL;
+}
+
+void
+mono_interp_set_breakpoint (MonoJitInfo *jinfo, gpointer ip)
+{
+ g_assert_not_reached ();
+}
+
+void
+mono_interp_clear_breakpoint (MonoJitInfo *jinfo, gpointer ip)
+{
+ g_assert_not_reached ();
+}
+
+MonoJitInfo*
+mono_interp_frame_get_jit_info (MonoInterpFrameHandle frame)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_interp_frame_get_ip (MonoInterpFrameHandle frame)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_interp_frame_get_arg (MonoInterpFrameHandle frame, int pos)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_interp_frame_get_local (MonoInterpFrameHandle frame, int pos)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gpointer
+mono_interp_frame_get_this (MonoInterpFrameHandle frame)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
+
+void
+mono_interp_start_single_stepping (void)
+{
+}
+
+void
+mono_interp_stop_single_stepping (void)
+{
+}
+
+void
+mono_interp_set_resume_state (MonoJitTlsData *jit_tls, MonoException *ex, MonoInterpFrameHandle interp_frame, gpointer handler_ip)
+{
+ g_assert_not_reached ();
+}
+
+void
+mono_interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip)
+{
+ g_assert_not_reached ();
+}
+
+void
+mono_interp_frame_iter_init (MonoInterpStackIter *iter, gpointer interp_exit_data)
+{
+ g_assert_not_reached ();
+}
+
+gboolean
+mono_interp_frame_iter_next (MonoInterpStackIter *iter, StackFrameInfo *frame)
+{
+ g_assert_not_reached ();
+ return FALSE;
+}
+
+#endif
+
#include <mono/mini/mini.h>
#include <mono/mini/jit-icalls.h>
+#include <mono/mini/debugger-agent.h>
#ifdef TARGET_ARM
#include <mono/mini/mini-arm.h>
* Used for testing.
*/
GSList *jit_classes;
+/* If TRUE, interpreted code will be interrupted at function entry/backward branches */
+static gboolean ss_enabled;
void ves_exec_method (MonoInvocation *frame);
/* Set the current execution state to the resume state in context */
#define SET_RESUME_STATE(context) do { \
ip = (context)->handler_ip; \
+ if (frame->ex) { \
sp->data.p = frame->ex; \
++sp; \
+ } \
frame->ex = NULL; \
(context)->has_resume_state = 0; \
(context)->handler_frame = NULL; \
goto main_loop; \
} while (0)
+static void
+set_context (ThreadContext *context)
+{
+ MonoJitTlsData *jit_tls;
+
+ mono_native_tls_set_value (thread_context_id, context);
+ jit_tls = mono_tls_get_jit_tls ();
+ if (jit_tls)
+ jit_tls->interp_context = context;
+}
+
static void
ves_real_abort (int line, MonoMethod *mh,
const unsigned short *ip, stackval *stack, stackval *sp)
THROW_EX (mono_get_exception_execution_engine (NULL), ip); \
} while (0);
+static RuntimeMethod*
+lookup_runtime_method (MonoDomain *domain, MonoMethod *method)
+{
+ RuntimeMethod *rtm;
+ MonoJitDomainInfo *info;
+
+ info = domain_jit_info (domain);
+ mono_domain_jit_code_hash_lock (domain);
+ rtm = mono_internal_hash_table_lookup (&info->interp_code_hash, method);
+ mono_domain_jit_code_hash_unlock (domain);
+ return rtm;
+}
+
RuntimeMethod*
mono_interp_get_runtime_method (MonoDomain *domain, MonoMethod *method, MonoError *error)
{
return mono_interp_get_runtime_method (domain, method, error);
}
+/*
+ * interp_push_lmf:
+ *
+ * Push an LMF frame on the LMF stack
+ * to mark the transition to native code.
+ * This is needed for the native code to
+ * be able to do stack walks.
+ */
+static void
+interp_push_lmf (MonoLMFExt *ext, MonoInvocation *frame)
+{
+ memset (ext, 0, sizeof (MonoLMFExt));
+ ext->interp_exit = TRUE;
+ ext->interp_exit_data = frame;
+
+ mono_push_lmf (ext);
+}
+
+static void
+interp_pop_lmf (MonoLMFExt *ext)
+{
+ mono_pop_lmf (&ext->lmf);
+}
+
static inline RuntimeMethod*
get_virtual_method (MonoDomain *domain, RuntimeMethod *runtime_method, MonoObject *obj)
{
context->current_frame = frame;
context->managed_code = 0;
- /*
- * Push an LMF frame on the LMF stack
- * to mark the transition to native code.
- */
- memset (&ext, 0, sizeof (ext));
- ext.interp_exit = TRUE;
- ext.interp_exit_data = frame;
-
- mono_push_lmf (&ext);
+ interp_push_lmf (&ext, frame);
mono_interp_enter_icall_trampoline (addr, margs);
- mono_pop_lmf (&ext.lmf);
+ interp_pop_lmf (&ext);
context->managed_code = 1;
/* domain can only be changed by native code */
int i, type, isobject = 0;
void *ret = NULL;
stackval result;
- stackval *args = alloca (sizeof (stackval) * (sig->param_count + !!sig->hasthis));
+ stackval *args;
ThreadContext context_struct;
MonoInvocation *old_frame = NULL;
jmp_buf env;
context->domain = mono_domain_get ();
context->current_frame = old_frame;
context->managed_code = 0;
- } else
- mono_native_tls_set_value (thread_context_id, NULL);
+ } else
+ set_context (NULL);
if (exc != NULL)
*exc = (MonoObject *)frame.ex;
return retval;
context_struct.base_frame = &frame;
context_struct.env_frame = &frame;
context_struct.current_env = &env;
- mono_native_tls_set_value (thread_context_id, context);
+ set_context (context);
}
else
old_frame = context->current_frame;
break;
}
+ args = alloca (sizeof (stackval) * (sig->param_count + !!sig->hasthis));
if (sig->hasthis)
args [0].data.p = obj;
if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
method = mono_marshal_get_native_wrapper (method, FALSE, FALSE);
INIT_FRAME (&frame,context->current_frame,args,&result,mono_get_root_domain (),method,error);
+
if (exc)
frame.invoke_trap = 1;
context->managed_code = 1;
ves_exec_method_with_context (&frame, context, NULL, NULL, -1);
context->managed_code = 0;
if (context == &context_struct)
- mono_native_tls_set_value (thread_context_id, NULL);
+ set_context (NULL);
else
context->current_frame = old_frame;
if (frame.ex != NULL) {
memset (context, 0, sizeof (ThreadContext));
context_struct.base_frame = &frame;
context_struct.env_frame = &frame;
- mono_native_tls_set_value (thread_context_id, context);
- }
- else
+ set_context (context);
+ } else {
old_frame = context->current_frame;
+ }
context->domain = mono_domain_get ();
args = alloca (sizeof (stackval) * (sig->param_count + (sig->hasthis ? 1 : 0)));
ves_exec_method_with_context (&frame, context, NULL, NULL, -1);
context->managed_code = 0;
if (context == &context_struct)
- mono_native_tls_set_value (thread_context_id, NULL);
+ set_context (NULL);
else
context->current_frame = old_frame;
g_print ("(%p) Transforming %s\n", mono_thread_internal_current (), mn);
g_free (mn);
#endif
+
+ MonoLMFExt ext;
+
+ /* Use the parent frame as the current frame is not complete yet */
+ interp_push_lmf (&ext, frame->parent);
+
frame->ex = mono_interp_transform_method (frame->runtime_method, context);
context->managed_code = 1;
+
+ interp_pop_lmf (&ext);
+
if (frame->ex) {
rtm = NULL;
ip = NULL;
vtalloc = vt_sp;
#endif
locals = (unsigned char *) vt_sp + rtm->vt_stack_size;
+ frame->locals = locals;
child_frame.parent = frame;
if (filter_exception) {
MINT_IN_CASE(MINT_NOP)
++ip;
MINT_IN_BREAK;
- MINT_IN_CASE(MINT_BREAK)
+ MINT_IN_CASE(MINT_BREAK) {
++ip;
- G_BREAKPOINT (); /* this is not portable... */
+
+ MonoLMFExt ext;
+
+ interp_push_lmf (&ext, frame);
+
+ mono_debugger_agent_user_break ();
+
+ interp_pop_lmf (&ext);
MINT_IN_BREAK;
+ }
MINT_IN_CASE(MINT_LDNULL)
sp->data.p = NULL;
++ip;
vtalloc = vt_sp;
#endif
locals = vt_sp + rtm->vt_stack_size;
+ frame->locals = locals;
ip = rtm->new_body_start; /* bypass storing input args from callers frame */
MINT_IN_BREAK;
}
}
}
- /*
- * Push an LMF frame on the LMF stack
- * to mark the transition to compiled code.
- */
- memset (&ext, 0, sizeof (ext));
- ext.interp_exit = TRUE;
- ext.interp_exit_data = frame;
-
- mono_push_lmf (&ext);
+ interp_push_lmf (&ext, frame);
switch (pindex) {
case 0: {
break;
}
- mono_pop_lmf (&ext.lmf);
+ interp_pop_lmf (&ext);
if (context->has_resume_state) {
/*
goto exit_frame;
MINT_IN_CASE(MINT_RET_VOID)
if (sp > frame->stack)
- g_warning ("ret.void: more values on stack: %d", sp-frame->stack);
+ g_warning ("ret.void: more values on stack: %d %s", sp-frame->stack, mono_method_full_name (frame->runtime_method->method, TRUE));
goto exit_frame;
MINT_IN_CASE(MINT_RET_VT)
i32 = READ32(ip + 1);
gint offset;
ip += 2 * (guint32)sp->data.i;
offset = READ32 (ip);
- ip = st + offset;
+ ip = ip + offset;
} else {
ip = st;
}
MINT_IN_BREAK;
MINT_IN_CASE(MINT_CPOBJ) {
c = rtm->data_items[* (guint16 *)(ip + 1)];
- g_assert (c->byval_arg.type == MONO_TYPE_VALUETYPE);
+ g_assert (c->valuetype);
/* if this assertion fails, we need to add a write barrier */
g_assert (!MONO_TYPE_IS_REFERENCE (&c->byval_arg));
- stackval_from_data (&c->byval_arg, &sp [-2], sp [-1].data.p, FALSE);
+ if (c->byval_arg.type == MONO_TYPE_VALUETYPE)
+ stackval_from_data (&c->byval_arg, &sp [-2], sp [-1].data.p, FALSE);
+ else
+ stackval_from_data (&c->byval_arg, sp [-2].data.p, sp [-1].data.p, FALSE);
ip += 2;
sp -= 2;
MINT_IN_BREAK;
frame->ex_handler = NULL;
if (!sp->data.p)
sp->data.p = mono_get_exception_null_reference ();
+
THROW_EX ((MonoException *)sp->data.p, ip);
MINT_IN_BREAK;
MINT_IN_CASE(MINT_LDFLDA_UNSAFE)
++ip;
mono_jit_set_domain (context->original_domain);
MINT_IN_BREAK;
+ MINT_IN_CASE(MINT_SDB_INTR_LOC)
+ if (G_UNLIKELY (ss_enabled)) {
+ MonoLMFExt ext;
+ static void (*ss_tramp) (void);
+
+ if (!ss_tramp) {
+ void *tramp = mini_get_single_step_trampoline ();
+ mono_memory_barrier ();
+ ss_tramp = tramp;
+ }
+
+ /*
+ * Make this point to the MINT_SDB_SEQ_POINT instruction which follows this since
+ * the address of that instruction is stored as the seq point address.
+ */
+ frame->ip = ip + 1;
+
+ interp_push_lmf (&ext, frame);
+ /*
+ * Use the same trampoline as the JIT. This ensures that
+ * the debugger has the context for the last interpreter
+ * native frame.
+ */
+ ss_tramp ();
+ interp_pop_lmf (&ext);
+
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+ }
+ ++ip;
+ MINT_IN_BREAK;
+ MINT_IN_CASE(MINT_SDB_SEQ_POINT)
+ /* Just a placeholder for a breakpoint */
+ ++ip;
+ MINT_IN_BREAK;
+ MINT_IN_CASE(MINT_SDB_BREAKPOINT) {
+ MonoLMFExt ext;
+
+ static void (*bp_tramp) (void);
+ if (!bp_tramp) {
+ void *tramp = mini_get_breakpoint_trampoline ();
+ mono_memory_barrier ();
+ bp_tramp = tramp;
+ }
+
+ frame->ip = ip;
+
+ interp_push_lmf (&ext, frame);
+ /* Use the same trampoline as the JIT */
+ bp_tramp ();
+ interp_pop_lmf (&ext);
+
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
+ ++ip;
+ MINT_IN_BREAK;
+ }
#define RELOP(datamem, op) \
--sp; \
context_struct.current_env = &env;
context_struct.search_for_handler = 0;
context_struct.managed_code = 0;
- mono_native_tls_set_value (thread_context_id, context);
+ set_context (context);
}
frame->ip = NULL;
frame->parent = context->current_frame;
mono_unhandled_exception ((MonoObject*)frame->ex);
}
if (context->base_frame == frame)
- mono_native_tls_set_value (thread_context_id, NULL);
+ set_context (NULL);
else
context->current_frame = frame->parent;
}
mono_interp_init ()
{
mono_native_tls_alloc (&thread_context_id, NULL);
- mono_native_tls_set_value (thread_context_id, NULL);
+ set_context (NULL);
mono_interp_transform_init ();
}
* Set the state the interpeter will continue to execute from after execution returns to the interpreter.
*/
void
-mono_interp_set_resume_state (MonoException *ex, StackFrameInfo *frame, gpointer handler_ip)
+mono_interp_set_resume_state (MonoJitTlsData *jit_tls, MonoException *ex, MonoInterpFrameHandle interp_frame, gpointer handler_ip)
{
- ThreadContext *context = mono_native_tls_get_value (thread_context_id);
+ ThreadContext *context;
+
+ g_assert (jit_tls);
+ context = jit_tls->interp_context;
+ g_assert (context);
context->has_resume_state = TRUE;
- context->handler_frame = frame->interp_frame;
+ context->handler_frame = interp_frame;
/* This is on the stack, so it doesn't need a wbarrier */
context->handler_frame->ex = ex;
context->handler_ip = handler_ip;
memset (frame, 0, sizeof (StackFrameInfo));
/* pinvoke frames doesn't have runtime_method set */
- while (iframe && !iframe->runtime_method)
+ while (iframe && !(iframe->runtime_method && iframe->runtime_method->code))
iframe = iframe->parent;
if (!iframe)
return FALSE;
frame->type = FRAME_TYPE_INTERP;
+ // FIXME:
+ frame->domain = mono_domain_get ();
frame->interp_frame = iframe;
frame->method = iframe->runtime_method->method;
frame->actual_method = frame->method;
/* This is the offset in the interpreter IR */
- frame->native_offset = iframe->ip - iframe->runtime_method->code;
+ frame->native_offset = (guint8*)iframe->ip - (guint8*)iframe->runtime_method->code;
frame->ji = iframe->runtime_method->jinfo;
stack_iter->current = iframe->parent;
return TRUE;
}
+
+MonoJitInfo*
+mono_interp_find_jit_info (MonoDomain *domain, MonoMethod *method)
+{
+ RuntimeMethod* rtm;
+
+ rtm = lookup_runtime_method (domain, method);
+ if (rtm)
+ return rtm->jinfo;
+ else
+ return NULL;
+}
+
+void
+mono_interp_set_breakpoint (MonoJitInfo *jinfo, gpointer ip)
+{
+ guint16 *code = (guint16*)ip;
+ g_assert (*code == MINT_SDB_SEQ_POINT);
+ *code = MINT_SDB_BREAKPOINT;
+}
+
+void
+mono_interp_clear_breakpoint (MonoJitInfo *jinfo, gpointer ip)
+{
+ guint16 *code = (guint16*)ip;
+ g_assert (*code == MINT_SDB_BREAKPOINT);
+ *code = MINT_SDB_SEQ_POINT;
+}
+
+MonoJitInfo*
+mono_interp_frame_get_jit_info (MonoInterpFrameHandle frame)
+{
+ MonoInvocation *iframe = (MonoInvocation*)frame;
+
+ g_assert (iframe->runtime_method);
+ return iframe->runtime_method->jinfo;
+}
+
+gpointer
+mono_interp_frame_get_ip (MonoInterpFrameHandle frame)
+{
+ MonoInvocation *iframe = (MonoInvocation*)frame;
+
+ g_assert (iframe->runtime_method);
+ return (gpointer)iframe->ip;
+}
+
+gpointer
+mono_interp_frame_get_arg (MonoInterpFrameHandle frame, int pos)
+{
+ MonoInvocation *iframe = (MonoInvocation*)frame;
+
+ g_assert (iframe->runtime_method);
+
+ int arg_offset = iframe->runtime_method->arg_offsets [pos + (iframe->runtime_method->hasthis ? 1 : 0)];
+
+ return iframe->args + arg_offset;
+}
+
+gpointer
+mono_interp_frame_get_local (MonoInterpFrameHandle frame, int pos)
+{
+ MonoInvocation *iframe = (MonoInvocation*)frame;
+
+ g_assert (iframe->runtime_method);
+
+ return iframe->locals + iframe->runtime_method->local_offsets [pos];
+}
+
+gpointer
+mono_interp_frame_get_this (MonoInterpFrameHandle frame)
+{
+ MonoInvocation *iframe = (MonoInvocation*)frame;
+
+ g_assert (iframe->runtime_method);
+ g_assert (iframe->runtime_method->hasthis);
+
+ int arg_offset = iframe->runtime_method->arg_offsets [0];
+
+ return iframe->args + arg_offset;
+}
+
+void
+mono_interp_start_single_stepping (void)
+{
+ ss_enabled = TRUE;
+}
+
+void
+mono_interp_stop_single_stepping (void)
+{
+ ss_enabled = FALSE;
+}
gpointer dummy [8];
};
+typedef gpointer MonoInterpFrameHandle;
+
int
mono_interp_regression_list (int verbose, int count, char *images []);
interp_walk_stack_with_ctx (MonoInternalStackWalk func, MonoContext *ctx, MonoUnwindOptions options, void *user_data);
void
-mono_interp_set_resume_state (MonoException *ex, StackFrameInfo *frame, gpointer handler_ip);
+mono_interp_set_resume_state (MonoJitTlsData *jit_tls, MonoException *ex, MonoInterpFrameHandle interp_frame, gpointer handler_ip);
void
mono_interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip);
gboolean
mono_interp_frame_iter_next (MonoInterpStackIter *iter, StackFrameInfo *frame);
+MonoJitInfo*
+mono_interp_find_jit_info (MonoDomain *domain, MonoMethod *method);
+
+void
+mono_interp_set_breakpoint (MonoJitInfo *jinfo, gpointer ip);
+
+void
+mono_interp_clear_breakpoint (MonoJitInfo *jinfo, gpointer ip);
+
+MonoJitInfo*
+mono_interp_frame_get_jit_info (MonoInterpFrameHandle frame);
+
+gpointer
+mono_interp_frame_get_ip (MonoInterpFrameHandle frame);
+
+gpointer
+mono_interp_frame_get_arg (MonoInterpFrameHandle frame, int pos);
+
+gpointer
+mono_interp_frame_get_local (MonoInterpFrameHandle frame, int pos);
+
+gpointer
+mono_interp_frame_get_this (MonoInterpFrameHandle frame);
+
+void
+mono_interp_start_single_stepping (void);
+
+void
+mono_interp_stop_single_stepping (void);
+
#endif /* __MONO_MINI_INTERPRETER_H__ */
if (i > 0)
g_print (", ");
offset = (gint32)READ32 (p);
- g_print ("IL_%04x", ip - base + 3 + 2 * sval + offset);
+ g_print ("IL_%04x", p + offset);
p += 2;
}
g_print (")");
// FIXME: MintOp
OPDEF(MINT_JIT_CALL, "mono_jit_call", 2, MintOpNoArgs)
+OPDEF(MINT_SDB_INTR_LOC, "sdb_intr_loc", 1, MintOpNoArgs)
+OPDEF(MINT_SDB_SEQ_POINT, "sdb_seq_point", 1, MintOpNoArgs)
+OPDEF(MINT_SDB_BREAKPOINT, "sdb_breakpoint", 1, MintOpNoArgs)
#include <mono/metadata/marshal.h>
#include <mono/metadata/profiler-private.h>
#include <mono/metadata/tabledefs.h>
+#include <mono/metadata/seq-points-data.h>
#include <mono/mini/mini.h>
unsigned char flags;
} StackInfo;
+typedef struct {
+ guint8 *ip;
+ GSList *preds;
+ GSList *seq_points;
+ SeqPoint *last_seq_point;
+
+ // This will hold a list of last sequence points of incoming basic blocks
+ SeqPoint **pred_seq_points;
+ guint num_pred_seq_points;
+} InterpBasicBlock;
+
+typedef enum {
+ RELOC_SHORT_BRANCH,
+ RELOC_LONG_BRANCH,
+ RELOC_SWITCH
+} RelocType;
+
+typedef struct {
+ RelocType type;
+ /* In the interpreter IR */
+ int offset;
+ /* In the IL code */
+ int target;
+} Reloc;
+
typedef struct
{
MonoMethod *method;
const unsigned char *in_start;
int code_size;
int *in_offsets;
- int *forward_refs;
StackInfo **stack_state;
int *stack_height;
int *vt_stack_size;
void **data_items;
GHashTable *data_hash;
int *clause_indexes;
+ gboolean gen_sdb_seq_points;
+ GPtrArray *seq_points;
+ InterpBasicBlock **offset_to_bb;
+ InterpBasicBlock *entry_bb;
+ MonoMemPool *mempool;
+ GList *basic_blocks;
+ GPtrArray *relocs;
+ gboolean verbose_level;
} TransformData;
#define MINT_TYPE_I1 0
} while (0)
static void
-handle_branch(TransformData *td, int short_op, int long_op, int offset)
+handle_branch (TransformData *td, int short_op, int long_op, int offset)
{
int shorten_branch = 0;
int target = td->ip + offset - td->il_code;
shorten_branch = 1;
}
} else {
- int prev = td->forward_refs [target];
- td->forward_refs [td->ip - td->il_code] = prev;
- td->forward_refs [target] = td->ip - td->il_code;
- offset = 0;
+ offset = 0xffff;
if (td->header->code_size <= 25000) /* FIX to be precise somehow? */
shorten_branch = 1;
+
+ Reloc *reloc = mono_mempool_alloc0 (td->mempool, sizeof (Reloc));
+ if (shorten_branch)
+ reloc->type = RELOC_SHORT_BRANCH;
+ else
+ reloc->type = RELOC_LONG_BRANCH;
+ reloc->offset = td->new_ip - td->new_code;
+ reloc->target = target;
+ g_ptr_array_add (td->relocs, reloc);
}
if (shorten_branch) {
ADD_CODE(td, short_op);
WRITE32(td, &size);
} else {
g_assert (mt < MINT_TYPE_VT);
- if (mt == MINT_TYPE_I4 && !td->is_bb_start [td->in_start - td->il_code] && td->last_new_ip != NULL &&
+ if (!td->gen_sdb_seq_points &&
+ mt == MINT_TYPE_I4 && !td->is_bb_start [td->in_start - td->il_code] && td->last_new_ip != NULL &&
td->last_new_ip [0] == MINT_STLOC_I4 && td->last_new_ip [1] == offset) {
td->last_new_ip [0] = MINT_STLOC_NP_I4;
- } else if (mt == MINT_TYPE_O && !td->is_bb_start [td->in_start - td->il_code] && td->last_new_ip != NULL &&
- td->last_new_ip [0] == MINT_STLOC_O && td->last_new_ip [1] == offset) {
+ } else if (!td->gen_sdb_seq_points &&
+ mt == MINT_TYPE_O && !td->is_bb_start [td->in_start - td->il_code] && td->last_new_ip != NULL &&
+ td->last_new_ip [0] == MINT_STLOC_O && td->last_new_ip [1] == offset) {
td->last_new_ip [0] = MINT_STLOC_NP_O;
} else {
ADD_CODE(td, MINT_LDLOC_I1 + (mt - MINT_TYPE_I1));
else
target_method = (MonoMethod *)mono_method_get_wrapper_data (method, token);
csignature = mono_method_signature (target_method);
- if (target_method->klass == mono_defaults.string_class) {
- if (target_method->name [0] == 'g') {
- if (strcmp (target_method->name, "get_Chars") == 0)
- op = MINT_GETCHR;
- else if (strcmp (target_method->name, "get_Length") == 0)
- op = MINT_STRLEN;
- }
- } else if (mono_class_is_subclass_of (target_method->klass, mono_defaults.array_class, FALSE)) {
- if (!strcmp (target_method->name, "get_Rank")) {
- op = MINT_ARRAY_RANK;
- } else if (!strcmp (target_method->name, "get_Length")) {
- op = MINT_LDLEN;
- } else if (!strcmp (target_method->name, "Address")) {
- op = readonly ? MINT_LDELEMA : MINT_LDELEMA_TC;
- }
- } else if (target_method && generic_context) {
+
+ if (generic_context) {
csignature = mono_inflate_generic_signature (csignature, generic_context, &error);
mono_error_cleanup (&error); /* FIXME: don't swallow the error */
target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, &error);
csignature = mono_method_signature (target_method);
}
+ /* Intrinsics */
+ if (target_method) {
+ if (target_method->klass == mono_defaults.string_class) {
+ if (target_method->name [0] == 'g') {
+ if (strcmp (target_method->name, "get_Chars") == 0)
+ op = MINT_GETCHR;
+ else if (strcmp (target_method->name, "get_Length") == 0)
+ op = MINT_STRLEN;
+ }
+ } else if (mono_class_is_subclass_of (target_method->klass, mono_defaults.array_class, FALSE)) {
+ if (!strcmp (target_method->name, "get_Rank")) {
+ op = MINT_ARRAY_RANK;
+ } else if (!strcmp (target_method->name, "get_Length")) {
+ op = MINT_LDLEN;
+ } else if (!strcmp (target_method->name, "Address")) {
+ op = readonly ? MINT_LDELEMA : MINT_LDELEMA_TC;
+ }
+ } else if (target_method->klass->image == mono_defaults.corlib &&
+ (strcmp (target_method->klass->name_space, "System.Diagnostics") == 0) &&
+ (strcmp (target_method->klass->name, "Debugger") == 0)) {
+ if (!strcmp (target_method->name, "Break") && csignature->param_count == 0) {
+ if (mini_should_insert_breakpoint (method))
+ op = MINT_BREAK;
+ }
+ }
+ }
+
if (constrained_class) {
if (constrained_class->enumtype && !strcmp (target_method->name, "GetHashCode")) {
/* Use the corresponding method from the base type to avoid boxing */
mono_class_init (target_method->klass);
CHECK_STACK (td, csignature->param_count + csignature->hasthis);
- if (!calli && (!virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) &&
+ if (!calli && op == -1 && (!virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) &&
(target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 &&
(target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0 &&
!(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING)) {
if (/*mono_metadata_signature_equal (method->signature, target_method->signature) */ method == target_method && *(td->ip + 5) == CEE_RET) {
int offset;
- if (mono_interp_traceopt)
+ if (td->verbose_level)
g_print ("Optimize tail call of %s.%s\n", target_method->klass->name, target_method->name);
for (i = csignature->param_count - 1 + !!csignature->hasthis; i >= 0; --i)
} else {
/* mheader might not exist if this is a delegate invoc, etc */
if (mheader && *mheader->code == CEE_RET && called_inited) {
- if (mono_interp_traceopt)
+ if (td->verbose_level)
g_print ("Inline (empty) call of %s.%s\n", target_method->klass->name, target_method->name);
for (i = 0; i < csignature->param_count; i++) {
ADD_CODE (td, MINT_POP); /*FIX: vt */
return field;
}
+static InterpBasicBlock*
+get_bb (TransformData *td, InterpBasicBlock *cbb, unsigned char *ip)
+{
+ int offset = ip - td->il_code;
+ InterpBasicBlock *bb = td->offset_to_bb [offset];
+
+ if (!bb) {
+ bb = mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock));
+ bb->ip = ip;
+ td->offset_to_bb [offset] = bb;
+
+ td->basic_blocks = g_list_append_mempool (td->mempool, td->basic_blocks, bb);
+ }
+
+ if (cbb)
+ bb->preds = g_slist_prepend_mempool (td->mempool, bb->preds, cbb);
+ return bb;
+}
+
+/*
+ * get_basic_blocks:
+ *
+ * Compute the set of IL level basic blocks.
+ */
+static void
+get_basic_blocks (TransformData *td)
+{
+ guint8 *start = (guint8*)td->il_code;
+ guint8 *end = (guint8*)td->il_code + td->code_size;
+ guint8 *ip = start;
+ unsigned char *target;
+ int i;
+ guint cli_addr;
+ const MonoOpcode *opcode;
+ InterpBasicBlock *cbb;
+
+ td->offset_to_bb = mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * (end - start + 1));
+ td->entry_bb = cbb = get_bb (td, NULL, start);
+
+ while (ip < end) {
+ cli_addr = ip - start;
+ td->offset_to_bb [cli_addr] = cbb;
+ i = mono_opcode_value ((const guint8 **)&ip, end);
+ opcode = &mono_opcodes [i];
+ switch (opcode->argument) {
+ case MonoInlineNone:
+ ip++;
+ break;
+ case MonoInlineString:
+ case MonoInlineType:
+ case MonoInlineField:
+ case MonoInlineMethod:
+ case MonoInlineTok:
+ case MonoInlineSig:
+ case MonoShortInlineR:
+ case MonoInlineI:
+ ip += 5;
+ break;
+ case MonoInlineVar:
+ ip += 3;
+ break;
+ case MonoShortInlineVar:
+ case MonoShortInlineI:
+ ip += 2;
+ break;
+ case MonoShortInlineBrTarget:
+ target = start + cli_addr + 2 + (signed char)ip [1];
+ get_bb (td, cbb, target);
+ ip += 2;
+ cbb = get_bb (td, cbb, ip);
+ break;
+ case MonoInlineBrTarget:
+ target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
+ get_bb (td, cbb, target);
+ ip += 5;
+ cbb = get_bb (td, cbb, ip);
+ break;
+ case MonoInlineSwitch: {
+ guint32 n = read32 (ip + 1);
+ guint32 j;
+ ip += 5;
+ cli_addr += 5 + 4 * n;
+ target = start + cli_addr;
+ get_bb (td, cbb, target);
+
+ for (j = 0; j < n; ++j) {
+ target = start + cli_addr + (gint32)read32 (ip);
+ get_bb (td, cbb, target);
+ ip += 4;
+ }
+ cbb = get_bb (td, cbb, ip);
+ break;
+ }
+ case MonoInlineR:
+ case MonoInlineI8:
+ ip += 9;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+}
+
static void
interp_save_debug_info (RuntimeMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers)
{
*/
dinfo = g_new0 (MonoDebugMethodJitInfo, 1);
+ dinfo->num_params = rtm->param_count;
+ dinfo->params = g_new0 (MonoDebugVarInfo, dinfo->num_params);
dinfo->num_locals = header->num_locals;
dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals);
dinfo->code_start = (guint8*)rtm->code;
dinfo->code_size = td->new_ip - td->new_code;
dinfo->epilogue_begin = 0;
- dinfo->has_var_info = FALSE;
+ dinfo->has_var_info = TRUE;
dinfo->num_line_numbers = line_numbers->len;
dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers);
+
+ for (i = 0; i < dinfo->num_params; i++) {
+ MonoDebugVarInfo *var = &dinfo->params [i];
+ var->type = rtm->param_types [i];
+ }
+ for (i = 0; i < dinfo->num_locals; i++) {
+ MonoDebugVarInfo *var = &dinfo->locals [i];
+ var->type = header->locals [i];
+ }
+
for (i = 0; i < dinfo->num_line_numbers; i++)
dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i);
mono_debug_add_method (rtm->method, dinfo, mono_domain_get ());
mono_debug_free_method_jit_info (dinfo);
}
+/* Same as the code in seq-points.c */
+static void
+insert_pred_seq_point (SeqPoint *last_sp, SeqPoint *sp, GSList **next)
+{
+ GSList *l;
+ int src_index = last_sp->next_offset;
+ int dst_index = sp->next_offset;
+
+ /* bb->in_bb might contain duplicates */
+ for (l = next [src_index]; l; l = l->next)
+ if (GPOINTER_TO_UINT (l->data) == dst_index)
+ break;
+ if (!l)
+ next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
+}
+
+static void
+recursively_make_pred_seq_points (TransformData *td, InterpBasicBlock *bb)
+{
+ const gpointer MONO_SEQ_SEEN_LOOP = GINT_TO_POINTER(-1);
+ GSList *l;
+
+ GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
+ GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
+
+ // Insert/remove sentinel into the memoize table to detect loops containing bb
+ bb->pred_seq_points = MONO_SEQ_SEEN_LOOP;
+
+ for (l = bb->preds; l; l = l->next) {
+ InterpBasicBlock *in_bb = l->data;
+
+ // This bb has the last seq point, append it and continue
+ if (in_bb->last_seq_point != NULL) {
+ predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
+ continue;
+ }
+
+ // We've looped or handled this before, exit early.
+ // No last sequence points to find.
+ if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
+ continue;
+
+ // Take sequence points from incoming basic blocks
+
+ if (in_bb == td->entry_bb)
+ continue;
+
+ if (in_bb->pred_seq_points == NULL)
+ recursively_make_pred_seq_points (td, in_bb);
+
+ // Union sequence points with incoming bb's
+ for (int i=0; i < in_bb->num_pred_seq_points; i++) {
+ if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
+ g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
+ g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
+ }
+ }
+ // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
+ }
+
+ g_hash_table_destroy (seen);
+
+ if (predecessors->len != 0) {
+ bb->pred_seq_points = mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint *) * predecessors->len);
+ bb->num_pred_seq_points = predecessors->len;
+
+ for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
+ bb->pred_seq_points [newer] = g_array_index (predecessors, gpointer, newer);
+ }
+ }
+
+ g_array_free (predecessors, TRUE);
+}
+
+static void
+collect_pred_seq_points (TransformData *td, InterpBasicBlock *bb, SeqPoint *seqp, GSList **next)
+{
+ // Doesn't have a last sequence point, must find from incoming basic blocks
+ if (bb->pred_seq_points == NULL && bb != td->entry_bb)
+ recursively_make_pred_seq_points (td, bb);
+
+ for (int i = 0; i < bb->num_pred_seq_points; i++)
+ insert_pred_seq_point (bb->pred_seq_points [i], seqp, next);
+
+ return;
+}
+
+static void
+save_seq_points (TransformData *td)
+{
+ RuntimeMethod *rtm = td->rtm;
+ GByteArray *array;
+ int i, seq_info_size;
+ MonoSeqPointInfo *info;
+ MonoDomain *domain = mono_domain_get ();
+ GSList **next = NULL;
+ GList *bblist;
+
+ if (!td->gen_sdb_seq_points)
+ return;
+
+ /*
+ * For each sequence point, compute the list of sequence points immediately
+ * following it, this is needed to implement 'step over' in the debugger agent.
+ * Similar to the code in mono_save_seq_point_info ().
+ */
+ for (i = 0; i < td->seq_points->len; ++i) {
+ SeqPoint *sp = g_ptr_array_index (td->seq_points, i);
+
+ /* Store the seq point index here temporarily */
+ sp->next_offset = i;
+ }
+ next = mono_mempool_alloc0 (td->mempool, sizeof (GList*) * td->seq_points->len);
+ for (bblist = td->basic_blocks; bblist; bblist = bblist->next) {
+ InterpBasicBlock *bb = bblist->data;
+
+ GSList *bb_seq_points = g_slist_reverse (bb->seq_points);
+ SeqPoint *last = NULL;
+ for (GSList *l = bb_seq_points; l; l = l->next) {
+ SeqPoint *sp = l->data;
+
+ if (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET)
+ /* Used to implement method entry/exit events */
+ continue;
+
+ if (last != NULL) {
+ /* Link with the previous seq point in the same bb */
+ next [last->next_offset] = g_slist_append_mempool (td->mempool, next [last->next_offset], GINT_TO_POINTER (sp->next_offset));
+ } else {
+ /* Link with the last bb in the previous bblocks */
+ collect_pred_seq_points (td, bb, sp, next);
+ }
+ last = sp;
+ }
+ }
+
+ /* Serialize the seq points into a byte array */
+ array = g_byte_array_new ();
+ SeqPoint zero_seq_point = {0};
+ SeqPoint* last_seq_point = &zero_seq_point;
+ for (i = 0; i < td->seq_points->len; ++i) {
+ SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
+
+ sp->next_offset = 0;
+ if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next [i], TRUE))
+ last_seq_point = sp;
+ }
+
+ if (td->verbose_level) {
+ g_print ("\nSEQ POINT MAP FOR %s: \n", td->method->name);
+
+ for (i = 0; i < td->seq_points->len; ++i) {
+ SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i);
+ GSList *l;
+
+ if (!next [i])
+ continue;
+
+ g_print ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
+ for (l = next [i]; l; l = l->next) {
+ int next_index = GPOINTER_TO_UINT (l->data);
+ g_print (" IL0x%x", ((SeqPoint*)g_ptr_array_index (td->seq_points, next_index))->il_offset);
+ }
+ g_print ("\n");
+ }
+ }
+
+ info = mono_seq_point_info_new (array->len, TRUE, array->data, TRUE, &seq_info_size);
+ mono_jit_stats.allocated_seq_points_size += seq_info_size;
+
+ g_byte_array_free (array, TRUE);
+
+ mono_domain_lock (domain);
+ g_hash_table_insert (domain_jit_info (domain)->seq_points, rtm->method, info);
+ mono_domain_unlock (domain);
+}
+
+static void
+emit_seq_point (TransformData *td, int il_offset, InterpBasicBlock *cbb, gboolean nonempty_stack)
+{
+ SeqPoint *seqp;
+
+ seqp = mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint));
+ seqp->il_offset = il_offset;
+ seqp->native_offset = (guint8*)td->new_ip - (guint8*)td->new_code;
+ if (nonempty_stack)
+ seqp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
+
+ ADD_CODE (td, MINT_SDB_SEQ_POINT);
+ g_ptr_array_add (td->seq_points, seqp);
+
+ cbb->seq_points = g_slist_prepend_mempool (td->mempool, cbb->seq_points, seqp);
+ cbb->last_seq_point = seqp;
+}
+
static void
generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, MonoGenericContext *generic_context)
{
TransformData td;
int generating_code = 1;
GArray *line_numbers;
+ MonoDebugMethodInfo *minfo;
+ MonoBitSet *seq_point_locs = NULL;
+ MonoBitSet *seq_point_set_locs = NULL;
+ gboolean sym_seq_points = FALSE;
+ InterpBasicBlock *bb_exit = NULL;
+ static gboolean verbose_method_inited;
+ static char* verbose_method_name;
+
+ if (!verbose_method_inited) {
+ verbose_method_name = getenv ("MONO_VERBOSE_METHOD");
+ verbose_method_inited = TRUE;
+ }
- memset(&td, 0, sizeof(td));
+ memset (&td, 0, sizeof(td));
td.method = method;
td.rtm = rtm;
td.is_bb_start = is_bb_start;
td.max_code_size = td.code_size;
td.new_code = (unsigned short *)g_malloc(td.max_code_size * sizeof(gushort));
td.new_code_end = td.new_code + td.max_code_size;
+ td.mempool = mono_mempool_new ();
td.in_offsets = g_malloc0(header->code_size * sizeof(int));
- td.forward_refs = g_malloc(header->code_size * sizeof(int));
td.stack_state = g_malloc0(header->code_size * sizeof(StackInfo *));
td.stack_height = g_malloc(header->code_size * sizeof(int));
td.vt_stack_size = g_malloc(header->code_size * sizeof(int));
td.data_items = NULL;
td.data_hash = g_hash_table_new (NULL, NULL);
td.clause_indexes = g_malloc (header->code_size * sizeof (int));
+ td.gen_sdb_seq_points = debug_options.gen_sdb_seq_points;
+ td.seq_points = g_ptr_array_new ();
+ td.relocs = g_ptr_array_new ();
+ td.verbose_level = mono_interp_traceopt;
rtm->data_items = td.data_items;
for (i = 0; i < header->code_size; i++) {
- td.forward_refs [i] = -1;
td.stack_height [i] = -1;
td.clause_indexes [i] = -1;
}
+
+ if (verbose_method_name) {
+ const char *name = verbose_method_name;
+
+ if ((strchr (name, '.') > name) || strchr (name, ':')) {
+ MonoMethodDesc *desc;
+
+ desc = mono_method_desc_new (name, TRUE);
+ if (mono_method_desc_full_match (desc, method)) {
+ td.verbose_level = 4;
+ }
+ mono_method_desc_free (desc);
+ } else {
+ if (strcmp (method->name, name) == 0)
+ td.verbose_level = 4;
+ }
+ }
+
+ if (td.gen_sdb_seq_points) {
+ get_basic_blocks (&td);
+
+ minfo = mono_debug_lookup_method (method);
+
+ if (minfo) {
+ MonoSymSeqPoint *sps;
+ int i, n_il_offsets;
+
+ mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
+ // FIXME: Free
+ seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td.mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
+ seq_point_set_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td.mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
+ sym_seq_points = TRUE;
+
+ for (i = 0; i < n_il_offsets; ++i) {
+ if (sps [i].il_offset < header->code_size)
+ mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
+ }
+ g_free (sps);
+ } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (method->klass->image)) {
+ /* Methods without line number info like auto-generated property accessors */
+ seq_point_locs = mono_bitset_new (header->code_size, 0);
+ seq_point_set_locs = mono_bitset_new (header->code_size, 0);
+ sym_seq_points = TRUE;
+ }
+ }
+
td.new_ip = td.new_code;
td.last_new_ip = NULL;
td.ip = header->code;
end = td.ip + header->code_size;
- if (mono_interp_traceopt) {
+ if (td.verbose_level) {
char *tmp = mono_disasm_code (NULL, method, td.ip, end);
char *name = mono_method_full_name (method, TRUE);
g_print ("Method %s, original code:\n", name);
}
}
+ if (sym_seq_points) {
+ InterpBasicBlock *cbb = td.offset_to_bb [0];
+ g_assert (cbb);
+ emit_seq_point (&td, METHOD_ENTRY_IL_OFFSET, cbb, FALSE);
+ }
+
while (td.ip < end) {
int in_offset;
td.in_start = td.ip;
MonoDebugLineNumberEntry lne;
- lne.native_offset = td.new_ip - td.new_code;
- lne.il_offset = td.ip - header->code;
+ lne.native_offset = (guint8*)td.new_ip - (guint8*)td.new_code;
+ lne.il_offset = in_offset;
g_array_append_val (line_numbers, lne);
- while (td.forward_refs [in_offset] >= 0) {
- int j = td.forward_refs [in_offset];
- int slot;
- td.forward_refs [in_offset] = td.forward_refs [j];
- if (td.in_offsets [j] < 0) {
- int old_switch_offset = -td.in_offsets [j];
- int new_switch_offset = td.in_offsets [old_switch_offset];
- int switch_case = (j - old_switch_offset - 5) / 4;
- int n_cases = read32 (header->code + old_switch_offset + 1);
- offset = (td.new_ip - td.new_code) - (new_switch_offset + 2 * n_cases + 3);
- slot = new_switch_offset + 3 + 2 * switch_case;
- td.new_code [slot] = * (unsigned short *)(&offset);
- td.new_code [slot + 1] = * ((unsigned short *)&offset + 1);
- } else {
- int op = td.new_code [td.in_offsets [j]];
- if (mono_interp_opargtype [op] == MintOpShortBranch) {
- offset = (td.new_ip - td.new_code) - td.in_offsets [j];
- g_assert (offset <= 32767);
- slot = td.in_offsets [j] + 1;
- td.new_code [slot] = offset;
- } else {
- offset = (td.new_ip - td.new_code) - td.in_offsets [j];
- slot = td.in_offsets [j] + 1;
- td.new_code [slot] = * (unsigned short *)(&offset);
- td.new_code [slot + 1] = * ((unsigned short *)&offset + 1);
- }
- }
- }
if (td.stack_height [in_offset] >= 0) {
g_assert (is_bb_start [in_offset]);
if (td.stack_height [in_offset] > 0)
++td.ip;
continue;
}
- if (mono_interp_traceopt > 1) {
+ if (td.verbose_level > 1) {
printf("IL_%04lx %s %-10s -> IL_%04lx, sp %ld, %s %-12s vt_sp %u (max %u)\n",
td.ip - td.il_code,
td.is_bb_start [td.ip - td.il_code] == 3 ? "<>" :
(td.sp > td.stack && (td.sp [-1].type == STACK_TYPE_O || td.sp [-1].type == STACK_TYPE_VT)) ? (td.sp [-1].klass == NULL ? "?" : td.sp [-1].klass->name) : "",
td.vt_sp, td.max_vt_sp);
}
+
+ if (sym_seq_points && mono_bitset_test_fast (seq_point_locs, td.ip - header->code)) {
+ InterpBasicBlock *cbb = td.offset_to_bb [td.ip - header->code];
+ g_assert (cbb);
+
+ /*
+ * Make methods interruptable at the beginning, and at the targets of
+ * backward branches.
+ */
+ if (in_offset == 0 || g_slist_length (cbb->preds) > 1)
+ ADD_CODE (&td, MINT_SDB_INTR_LOC);
+
+ emit_seq_point (&td, in_offset, cbb, FALSE);
+
+ mono_bitset_set_fast (seq_point_set_locs, td.ip - header->code);
+ }
+
+ if (sym_seq_points)
+ bb_exit = td.offset_to_bb [td.ip - header->code];
+
switch (*td.ip) {
case CEE_NOP:
/* lose it */
case CEE_CALLVIRT: /* Fall through */
case CEE_CALLI: /* Fall through */
case CEE_CALL: {
+ gboolean need_seq_point = FALSE;
+
+ if (sym_seq_points && !mono_bitset_test_fast (seq_point_locs, td.ip + 5 - header->code))
+ need_seq_point = TRUE;
+
interp_transform_call (&td, method, NULL, domain, generic_context, is_bb_start, body_start_offset, constrained_class, readonly);
+
+ if (need_seq_point) {
+ InterpBasicBlock *cbb = td.offset_to_bb [td.ip - header->code];
+ g_assert (cbb);
+
+ emit_seq_point (&td, td.ip - header->code, cbb, TRUE);
+ }
+
constrained_class = NULL;
readonly = FALSE;
break;
g_warning ("%s.%s: CEE_RET: more values on stack: %d", td.method->klass->name, td.method->name, td.sp - td.stack);
if (td.vt_sp != vt_size)
g_error ("%s.%s: CEE_RET: value type stack: %d vs. %d", td.method->klass->name, td.method->name, td.vt_sp, vt_size);
+
+ if (sym_seq_points) {
+ InterpBasicBlock *cbb = td.offset_to_bb [td.ip - header->code];
+ g_assert (cbb);
+ emit_seq_point (&td, METHOD_EXIT_IL_OFFSET, bb_exit, FALSE);
+ }
+
if (vt_size == 0)
SIMPLE_OP(td, signature->ret->type == MONO_TYPE_VOID ? MINT_RET_VOID : MINT_RET);
else {
case CEE_SWITCH: {
guint32 n;
const unsigned char *next_ip;
- const unsigned char *base_ip = td.ip;
- unsigned short *next_new_ip;
++td.ip;
n = read32 (td.ip);
ADD_CODE (&td, MINT_SWITCH);
WRITE32 (&td, &n);
td.ip += 4;
next_ip = td.ip + n * 4;
- next_new_ip = td.new_ip + n * 2;
--td.sp;
int stack_height = td.sp - td.stack;
for (i = 0; i < n; i++) {
if (stack_height > 0 && stack_height != td.stack_height [target])
g_warning ("SWITCH with back branch and non-empty stack");
#endif
- target = td.in_offsets [target] - (next_new_ip - td.new_code);
+ target = td.in_offsets [target] - (td.new_ip - td.new_code);
} else {
td.stack_height [target] = stack_height;
td.vt_stack_size [target] = td.vt_sp;
if (stack_height > 0)
td.stack_state [target] = g_memdup (td.stack, stack_height * sizeof (td.stack [0]));
- int prev = td.forward_refs [target];
- td.forward_refs [td.ip - td.il_code] = prev;
- td.forward_refs [target] = td.ip - td.il_code;
- td.in_offsets [td.ip - td.il_code] = - (base_ip - td.il_code);
+
+ Reloc *reloc = mono_mempool_alloc0 (td.mempool, sizeof (Reloc));
+ reloc->type = RELOC_SWITCH;
+ reloc->offset = td.new_ip - td.new_code;
+ reloc->target = target;
+ g_ptr_array_add (td.relocs, reloc);
+ target = 0xffff;
}
WRITE32 (&td, &target);
td.ip += 4;
MonoString *s;
token = mono_metadata_token_index (read32 (td.ip + 1));
td.ip += 5;
- if (method->wrapper_type != MONO_WRAPPER_NONE) {
- s = mono_string_new_wrapper(
- mono_method_get_wrapper_data (method, token));
- }
- else
+ if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
+ s = mono_method_get_wrapper_data (method, token);
+ } else if (method->wrapper_type != MONO_WRAPPER_NONE) {
+ s = mono_string_new_wrapper (mono_method_get_wrapper_data (method, token));
+ } else {
s = mono_ldstr (domain, image, token);
+ }
ADD_CODE(&td, MINT_LDSTR);
ADD_CODE(&td, get_data_item_index (&td, s));
PUSH_TYPE(&td, STACK_TYPE_O, mono_defaults.string_class);
++td.ip;
break;
case CEE_UNALIGNED_:
- ++td.ip;
- /* FIX: should do something? */;
+ td.ip += 2;
break;
case CEE_VOLATILE_:
++td.ip;
td.last_ip = td.in_start;
}
- if (mono_interp_traceopt) {
+ /* Handle relocations */
+ for (int i = 0; i < td.relocs->len; ++i) {
+ Reloc *reloc = g_ptr_array_index (td.relocs, i);
+
+ int offset = td.in_offsets [reloc->target] - reloc->offset;
+
+ switch (reloc->type) {
+ case RELOC_SHORT_BRANCH:
+ g_assert (td.new_code [reloc->offset + 1] == 0xffff);
+ td.new_code [reloc->offset + 1] = offset;
+ break;
+ case RELOC_LONG_BRANCH:
+ g_assert_not_reached ();
+ break;
+ case RELOC_SWITCH: {
+ guint16 *v = (guint16*)&offset;
+ td.new_code [reloc->offset] = *(guint16*)v;
+ td.new_code [reloc->offset + 1] = *(guint16*)(v + 1);
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+
+ if (td.verbose_level) {
const guint16 *p = td.new_code;
- printf("Runtime method: %p, VT stack size: %d\n", rtm, td.max_vt_sp);
+ printf("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method, TRUE), rtm, td.max_vt_sp);
printf("Calculated stack size: %d, stated size: %d\n", td.max_stack_height, header->max_stack);
while (p < td.new_ip) {
p = mono_interp_dis_mintop(td.new_code, p);
/* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
int jinfo_len = mono_jit_info_size (0, header->num_clauses, 0);
MonoJitInfo *jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len);
+ jinfo->is_interp = 1;
rtm->jinfo = jinfo;
mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len, 0, header->num_clauses, 0);
for (i = 0; i < jinfo->num_clauses; ++i) {
MonoExceptionClause *c = rtm->clauses + i;
ei->flags = c->flags;
- ei->try_start = rtm->code + c->try_offset;
- ei->try_end = rtm->code + c->try_offset + c->try_len;
- ei->handler_start = rtm->code + c->handler_offset;
+ ei->try_start = (guint8*)(rtm->code + c->try_offset);
+ ei->try_end = (guint8*)(rtm->code + c->try_offset + c->try_len);
+ ei->handler_start = (guint8*)(rtm->code + c->handler_offset);
if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
} else {
ei->data.catch_class = c->data.catch_class;
}
}
+ save_seq_points (&td);
+
g_free (td.in_offsets);
- g_free (td.forward_refs);
for (i = 0; i < header->code_size; ++i)
g_free (td.stack_state [i]);
g_free (td.stack_state);
g_free (td.stack);
g_hash_table_destroy (td.data_hash);
g_free (td.clause_indexes);
+ g_ptr_array_free (td.seq_points, TRUE);
g_array_free (line_numbers, TRUE);
+ g_ptr_array_free (td.relocs, TRUE);
+ mono_mempool_destroy (td.mempool);
}
static mono_mutex_t calc_section;
mono_os_mutex_lock(&calc_section);
if (runtime_method->transformed) {
mono_os_mutex_unlock(&calc_section);
- mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_OK);
+ mono_profiler_method_end_jit (method, runtime_method->jinfo, MONO_PROFILE_OK);
return NULL;
}
if (runtime_method->transformed) {
mono_os_mutex_unlock(&calc_section);
g_free (is_bb_start);
- mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_OK);
+ mono_profiler_method_end_jit (method, runtime_method->jinfo, MONO_PROFILE_OK);
return NULL;
}
g_free (is_bb_start);
- mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_OK);
+ // FIXME: Add a different callback ?
+ mono_profiler_method_end_jit (method, runtime_method->jinfo, MONO_PROFILE_OK);
runtime_method->transformed = TRUE;
mono_os_mutex_unlock(&calc_section);
}
void
-ves_icall_mono_delegate_ctor (MonoObject *this_obj, MonoObject *target, gpointer addr)
+ves_icall_mono_delegate_ctor (MonoObject *this_obj_raw, MonoObject *target_raw, gpointer addr)
{
+ HANDLE_FUNCTION_ENTER ();
MonoError error;
+ MONO_HANDLE_DCL (MonoObject, this_obj);
+ MONO_HANDLE_DCL (MonoObject, target);
mono_delegate_ctor (this_obj, target, addr, &error);
mono_error_set_pending_exception (&error);
+ HANDLE_FUNCTION_RETURN ();
}
gpointer
#ifndef DISABLE_JIT
+#include <mono/metadata/gc-internals.h>
#include <mono/utils/mono-memory-model.h>
#include "mini.h"
#include "ir-emit.h"
+#include "jit-icalls.h"
#define MAX_INLINE_COPIES 10
+#define MAX_INLINE_COPY_SIZE 10000
void
mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
{
int val_reg;
+ /*FIXME arbitrary hack to avoid unbound code expansion.*/
+ g_assert (size < MAX_INLINE_COPY_SIZE);
g_assert (val == 0);
-
- if (align == 0)
- align = 4;
+ g_assert (align > 0);
if ((size <= SIZEOF_REGISTER) && (size <= align)) {
switch (size) {
else
MONO_EMIT_NEW_ICONST (cfg, val_reg, val);
- if (align < 4) {
- /* This could be optimized further if neccesary */
- while (size >= 1) {
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
- offset += 1;
- size -= 1;
- }
- return;
- }
-
- if (!cfg->backend->no_unaligned_access && SIZEOF_REGISTER == 8) {
- if (offset % 8) {
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
- offset += 4;
- size -= 4;
- }
+ if (align < SIZEOF_VOID_P) {
+ if (align % 2 == 1)
+ goto set_1;
+ if (align % 4 == 2)
+ goto set_2;
+ if (SIZEOF_VOID_P == 8 && align % 8 == 4)
+ goto set_4;
+ }
+
+ //Unaligned offsets don't naturaly happen in the runtime, so it's ok to be conservative in how we copy
+ //We assume that input src and dest are be aligned to `align` so offset just worsen it
+ int offsets_mask = offset & 0x7; //we only care about the misalignment part
+ if (offsets_mask) {
+ if (offsets_mask % 2 == 1)
+ goto set_1;
+ if (offsets_mask % 4 == 2)
+ goto set_2;
+ if (SIZEOF_VOID_P == 8 && offsets_mask % 8 == 4)
+ goto set_4;
+ }
+
+ if (SIZEOF_REGISTER == 8) {
while (size >= 8) {
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, offset, val_reg);
offset += 8;
size -= 8;
}
- }
+ }
+set_4:
while (size >= 4) {
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
offset += 4;
size -= 4;
}
+
+
+set_2:
while (size >= 2) {
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, val_reg);
offset += 2;
size -= 2;
}
+
+set_1:
while (size >= 1) {
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
offset += 1;
{
int cur_reg;
- if (align == 0)
- align = 4;
-
/*FIXME arbitrary hack to avoid unbound code expansion.*/
- g_assert (size < 10000);
+ g_assert (size < MAX_INLINE_COPY_SIZE);
+ g_assert (align > 0);
+
+ if (align < SIZEOF_VOID_P) {
+ if (align == 4)
+ goto copy_4;
+ if (align == 2)
+ goto copy_2;
+ goto copy_1;
+ }
- if (align < 4) {
- /* This could be optimized further if neccesary */
- while (size >= 1) {
- cur_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
- doffset += 1;
- soffset += 1;
- size -= 1;
- }
+ //Unaligned offsets don't naturaly happen in the runtime, so it's ok to be conservative in how we copy
+ //We assume that input src and dest are be aligned to `align` so offset just worsen it
+ int offsets_mask = (doffset | soffset) & 0x7; //we only care about the misalignment part
+ if (offsets_mask) {
+ if (offsets_mask % 2 == 1)
+ goto copy_1;
+ if (offsets_mask % 4 == 2)
+ goto copy_2;
+ if (SIZEOF_VOID_P == 8 && offsets_mask % 8 == 4)
+ goto copy_4;
}
- if (!cfg->backend->no_unaligned_access && SIZEOF_REGISTER == 8) {
+
+ if (SIZEOF_REGISTER == 8) {
while (size >= 8) {
cur_reg = alloc_preg (cfg);
MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset);
soffset += 8;
size -= 8;
}
- }
+ }
+copy_4:
while (size >= 4) {
cur_reg = alloc_preg (cfg);
MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, cur_reg, srcreg, soffset);
soffset += 4;
size -= 4;
}
+
+copy_2:
while (size >= 2) {
cur_reg = alloc_preg (cfg);
MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, cur_reg, srcreg, soffset);
soffset += 2;
size -= 2;
}
+
+copy_1:
while (size >= 1) {
cur_reg = alloc_preg (cfg);
MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
/* FIXME: Optimize the case when src/dest is OP_LDADDR */
/* We can't do copies at a smaller granule than the provided alignment */
- if (size_ins || ((size / align > MAX_INLINE_COPIES) && !(cfg->opt & MONO_OPT_INTRINS))) {
+ if (size_ins || (size / align > MAX_INLINE_COPIES) || !(cfg->opt & MONO_OPT_INTRINS)) {
MonoInst *iargs [3];
iargs [0] = dest;
iargs [1] = src;
/* FIXME: Optimize the case when dest is OP_LDADDR */
/* We can't do copies at a smaller granule than the provided alignment */
- if (value_ins || size_ins || value != 0 || ((size / align > MAX_INLINE_COPIES) && !(cfg->opt & MONO_OPT_INTRINS))) {
+ if (value_ins || size_ins || value != 0 || (size / align > MAX_INLINE_COPIES) || !(cfg->opt & MONO_OPT_INTRINS)) {
MonoInst *iargs [3];
iargs [0] = dest;
mini_emit_memset_internal (cfg, dest, NULL, value, NULL, size, align);
}
+
+static void
+create_write_barrier_bitmap (MonoCompile *cfg, MonoClass *klass, unsigned *wb_bitmap, int offset)
+{
+ MonoClassField *field;
+ gpointer iter = NULL;
+
+ while ((field = mono_class_get_fields (klass, &iter))) {
+ int foffset;
+
+ if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
+ continue;
+ foffset = klass->valuetype ? field->offset - sizeof (MonoObject): field->offset;
+ if (mini_type_is_reference (mono_field_get_type (field))) {
+ g_assert ((foffset % SIZEOF_VOID_P) == 0);
+ *wb_bitmap |= 1 << ((offset + foffset) / SIZEOF_VOID_P);
+ } else {
+ MonoClass *field_class = mono_class_from_mono_type (field->type);
+ if (field_class->has_references)
+ create_write_barrier_bitmap (cfg, field_class, wb_bitmap, offset + foffset);
+ }
+ }
+}
+
+static gboolean
+mini_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align)
+{
+ int dest_ptr_reg, tmp_reg, destreg, srcreg, offset;
+ unsigned need_wb = 0;
+
+ if (align == 0)
+ align = 4;
+
+ /*types with references can't have alignment smaller than sizeof(void*) */
+ if (align < SIZEOF_VOID_P)
+ return FALSE;
+
+ if (size > 5 * SIZEOF_VOID_P)
+ return FALSE;
+
+ create_write_barrier_bitmap (cfg, klass, &need_wb, 0);
+
+ destreg = iargs [0]->dreg;
+ srcreg = iargs [1]->dreg;
+ offset = 0;
+
+ dest_ptr_reg = alloc_preg (cfg);
+ tmp_reg = alloc_preg (cfg);
+
+ /*tmp = dreg*/
+ EMIT_NEW_UNALU (cfg, iargs [0], OP_MOVE, dest_ptr_reg, destreg);
+
+ while (size >= SIZEOF_VOID_P) {
+ MonoInst *load_inst;
+ MONO_INST_NEW (cfg, load_inst, OP_LOAD_MEMBASE);
+ load_inst->dreg = tmp_reg;
+ load_inst->inst_basereg = srcreg;
+ load_inst->inst_offset = offset;
+ MONO_ADD_INS (cfg->cbb, load_inst);
+
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, dest_ptr_reg, 0, tmp_reg);
+
+ if (need_wb & 0x1)
+ mini_emit_write_barrier (cfg, iargs [0], load_inst);
+
+ offset += SIZEOF_VOID_P;
+ size -= SIZEOF_VOID_P;
+ need_wb >>= 1;
+
+ /*tmp += sizeof (void*)*/
+ if (size >= SIZEOF_VOID_P) {
+ NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dest_ptr_reg, dest_ptr_reg, SIZEOF_VOID_P);
+ MONO_ADD_INS (cfg->cbb, iargs [0]);
+ }
+ }
+
+ /* Those cannot be references since size < sizeof (void*) */
+ while (size >= 4) {
+ MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, tmp_reg, srcreg, offset);
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, tmp_reg);
+ offset += 4;
+ size -= 4;
+ }
+
+ while (size >= 2) {
+ MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, tmp_reg, srcreg, offset);
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, tmp_reg);
+ offset += 2;
+ size -= 2;
+ }
+
+ while (size >= 1) {
+ MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, tmp_reg, srcreg, offset);
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, tmp_reg);
+ offset += 1;
+ size -= 1;
+ }
+
+ return TRUE;
+}
+
+static void
+mini_emit_memory_copy_internal (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, int explicit_align, gboolean native)
+{
+ MonoInst *iargs [4];
+ int size;
+ guint32 align = 0;
+ MonoInst *size_ins = NULL;
+ MonoInst *memcpy_ins = NULL;
+
+ g_assert (klass);
+ /*
+ Fun fact about @native. It's false that @klass will have no ref when @native is true.
+ This happens in pinvoke2. What goes is that marshal.c uses CEE_MONO_LDOBJNATIVE and pass klass.
+ The actual stuff being copied will have no refs, but @klass might.
+ This means we can't assert !(klass->has_references && native).
+ */
+
+ if (cfg->gshared)
+ klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
+
+ /*
+ * This check breaks with spilled vars... need to handle it during verification anyway.
+ * g_assert (klass && klass == src->klass && klass == dest->klass);
+ */
+
+ if (mini_is_gsharedvt_klass (klass)) {
+ g_assert (!native);
+ size_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
+ memcpy_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_MEMCPY);
+ }
+
+ if (native)
+ size = mono_class_native_size (klass, &align);
+ else
+ size = mono_class_value_size (klass, &align);
+
+ if (!align)
+ align = SIZEOF_VOID_P;
+ if (explicit_align)
+ align = explicit_align;
+
+ if (mini_type_is_reference (&klass->byval_arg)) { // Refs *MUST* be naturally aligned
+ MonoInst *store, *load;
+ int dreg = alloc_ireg_ref (cfg);
+
+ NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, dreg, src->dreg, 0);
+ MONO_ADD_INS (cfg->cbb, load);
+
+ NEW_STORE_MEMBASE (cfg, store, OP_STORE_MEMBASE_REG, dest->dreg, 0, dreg);
+ MONO_ADD_INS (cfg->cbb, store);
+
+ mini_emit_write_barrier (cfg, dest, src);
+ } else if (cfg->gen_write_barriers && (klass->has_references || size_ins) && !native) { /* if native is true there should be no references in the struct */
+ /* Avoid barriers when storing to the stack */
+ if (!((dest->opcode == OP_ADD_IMM && dest->sreg1 == cfg->frame_reg) ||
+ (dest->opcode == OP_LDADDR))) {
+ int context_used;
+
+ iargs [0] = dest;
+ iargs [1] = src;
+
+ context_used = mini_class_check_context_used (cfg, klass);
+
+ /* It's ok to intrinsify under gsharing since shared code types are layout stable. */
+ if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && mini_emit_wb_aware_memcpy (cfg, klass, iargs, size, align)) {
+ } else if (size_ins || align < SIZEOF_VOID_P) {
+ if (context_used) {
+ iargs [2] = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
+ } else {
+ iargs [2] = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
+ if (!cfg->compile_aot)
+ mono_class_compute_gc_descriptor (klass);
+ }
+ if (size_ins)
+ mono_emit_jit_icall (cfg, mono_gsharedvt_value_copy, iargs);
+ else
+ mono_emit_jit_icall (cfg, mono_value_copy, iargs);
+ } else {
+ /* We don't unroll more than 5 stores to avoid code bloat. */
+ /*This is harmless and simplify mono_gc_get_range_copy_func */
+ size += (SIZEOF_VOID_P - 1);
+ size &= ~(SIZEOF_VOID_P - 1);
+
+ EMIT_NEW_ICONST (cfg, iargs [2], size);
+ mono_emit_jit_icall (cfg, mono_gc_get_range_copy_func (), iargs);
+ }
+ return;
+ }
+ }
+
+ if (size_ins) {
+ iargs [0] = dest;
+ iargs [1] = src;
+ iargs [2] = size_ins;
+ mini_emit_calli (cfg, mono_method_signature (mini_get_memcpy_method ()), iargs, memcpy_ins, NULL, NULL);
+ } else {
+ mini_emit_memcpy_const_size (cfg, dest, src, size, align);
+ }
+}
+
MonoInst*
mini_emit_memory_load (MonoCompile *cfg, MonoType *type, MonoInst *src, int offset, int ins_flag)
{
MonoInst *ins;
- EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, type, src->dreg, offset);
+ if (ins_flag & MONO_INST_UNALIGNED) {
+ MonoInst *addr, *tmp_var;
+ int align;
+ int size = mono_type_size (type, &align);
+
+ if (offset) {
+ MonoInst *add_offset;
+ NEW_BIALU_IMM (cfg, add_offset, OP_PADD_IMM, alloc_preg (cfg), src->dreg, offset);
+ MONO_ADD_INS (cfg->cbb, add_offset);
+ src = add_offset;
+ }
+
+ tmp_var = mono_compile_create_var (cfg, type, OP_LOCAL);
+ EMIT_NEW_VARLOADA (cfg, addr, tmp_var, tmp_var->inst_vtype);
+
+ mini_emit_memcpy_const_size (cfg, addr, src, size, 1);
+ EMIT_NEW_TEMPLOAD (cfg, ins, tmp_var->inst_c0);
+ } else {
+ EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, type, src->dreg, offset);
+ }
ins->flags |= ins_flag;
if (ins_flag & MONO_INST_VOLATILE) {
/* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
}
+
+ if (ins_flag & MONO_INST_UNALIGNED) {
+ MonoInst *addr, *mov, *tmp_var;
+
+ tmp_var = mono_compile_create_var (cfg, type, OP_LOCAL);
+ EMIT_NEW_TEMPSTORE (cfg, mov, tmp_var->inst_c0, value);
+ EMIT_NEW_VARLOADA (cfg, addr, tmp_var, tmp_var->inst_vtype);
+ mini_emit_memory_copy_internal (cfg, dest, addr, mono_class_from_mono_type (type), 1, FALSE);
+ }
+
/* FIXME: should check item at sp [1] is compatible with the type of the store. */
EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, type, dest->dreg, 0, value->dreg);
void
mini_emit_memory_copy_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoInst *size, int ins_flag)
{
- int align = SIZEOF_VOID_P;
+ int align = (ins_flag & MONO_INST_UNALIGNED) ? 1 : SIZEOF_VOID_P;
/*
* FIXME: It's unclear whether we should be emitting both the acquire
mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
}
- if ((cfg->opt & MONO_OPT_INTRINS) && (size->opcode == OP_ICONST) && size->inst_c0 < 10000) {
+ if ((cfg->opt & MONO_OPT_INTRINS) && (size->opcode == OP_ICONST)) {
mini_emit_memcpy_const_size (cfg, dest, src, size->inst_c0, align);
} else {
- if (cfg->verbose_level > 3)
- printf ("EMITING REGULAR COPY\n");
mini_emit_memcpy_internal (cfg, dest, src, size, 0, align);
}
void
mini_emit_memory_init_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *value, MonoInst *size, int ins_flag)
{
- int align = SIZEOF_VOID_P;
+ int align = (ins_flag & MONO_INST_UNALIGNED) ? 1 : SIZEOF_VOID_P;
if (ins_flag & MONO_INST_VOLATILE) {
/* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
}
+/*
+ * If @klass is a valuetype, emit code to copy a value with source address in @src and destination address in @dest.
+ * If @klass is a ref type, copy a pointer instead.
+ */
+
+void
+mini_emit_memory_copy (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native, int ins_flag)
+{
+ int explicit_align = 0;
+ if (ins_flag & MONO_INST_UNALIGNED)
+ explicit_align = 1;
+
+ /*
+ * FIXME: It's unclear whether we should be emitting both the acquire
+ * and release barriers for cpblk. It is technically both a load and
+ * store operation, so it seems like that's the sensible thing to do.
+ *
+ * FIXME: We emit full barriers on both sides of the operation for
+ * simplicity. We should have a separate atomic memcpy method instead.
+ */
+ if (ins_flag & MONO_INST_VOLATILE) {
+ /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
+ mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
+ }
+
+ mini_emit_memory_copy_internal (cfg, dest, src, klass, explicit_align, native);
+
+ if (ins_flag & MONO_INST_VOLATILE) {
+ /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
+ mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
+ }
+}
+
#endif
return memcpy_method;
}
-static void
-create_write_barrier_bitmap (MonoCompile *cfg, MonoClass *klass, unsigned *wb_bitmap, int offset)
-{
- MonoClassField *field;
- gpointer iter = NULL;
-
- while ((field = mono_class_get_fields (klass, &iter))) {
- int foffset;
-
- if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
- continue;
- foffset = klass->valuetype ? field->offset - sizeof (MonoObject): field->offset;
- if (mini_type_is_reference (mono_field_get_type (field))) {
- g_assert ((foffset % SIZEOF_VOID_P) == 0);
- *wb_bitmap |= 1 << ((offset + foffset) / SIZEOF_VOID_P);
- } else {
- MonoClass *field_class = mono_class_from_mono_type (field->type);
- if (field_class->has_references)
- create_write_barrier_bitmap (cfg, field_class, wb_bitmap, offset + foffset);
- }
- }
-}
-
void
mini_emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value)
{
if (!cfg->gen_write_barriers)
return;
+ //method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER && !MONO_INS_IS_PCONST_NULL (sp [1])
+
card_table = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask);
mono_gc_get_nursery (&nursery_shift_bits, &nursery_size);
EMIT_NEW_DUMMY_USE (cfg, dummy_use, value);
}
-gboolean
-mini_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align)
-{
- int dest_ptr_reg, tmp_reg, destreg, srcreg, offset;
- unsigned need_wb = 0;
-
- if (align == 0)
- align = 4;
-
- /*types with references can't have alignment smaller than sizeof(void*) */
- if (align < SIZEOF_VOID_P)
- return FALSE;
-
- if (size > 5 * SIZEOF_VOID_P)
- return FALSE;
-
- create_write_barrier_bitmap (cfg, klass, &need_wb, 0);
-
- destreg = iargs [0]->dreg;
- srcreg = iargs [1]->dreg;
- offset = 0;
-
- dest_ptr_reg = alloc_preg (cfg);
- tmp_reg = alloc_preg (cfg);
-
- /*tmp = dreg*/
- EMIT_NEW_UNALU (cfg, iargs [0], OP_MOVE, dest_ptr_reg, destreg);
-
- while (size >= SIZEOF_VOID_P) {
- MonoInst *load_inst;
- MONO_INST_NEW (cfg, load_inst, OP_LOAD_MEMBASE);
- load_inst->dreg = tmp_reg;
- load_inst->inst_basereg = srcreg;
- load_inst->inst_offset = offset;
- MONO_ADD_INS (cfg->cbb, load_inst);
-
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, dest_ptr_reg, 0, tmp_reg);
-
- if (need_wb & 0x1)
- mini_emit_write_barrier (cfg, iargs [0], load_inst);
-
- offset += SIZEOF_VOID_P;
- size -= SIZEOF_VOID_P;
- need_wb >>= 1;
-
- /*tmp += sizeof (void*)*/
- if (size >= SIZEOF_VOID_P) {
- NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dest_ptr_reg, dest_ptr_reg, SIZEOF_VOID_P);
- MONO_ADD_INS (cfg->cbb, iargs [0]);
- }
- }
-
- /* Those cannot be references since size < sizeof (void*) */
- while (size >= 4) {
- MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, tmp_reg, srcreg, offset);
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, tmp_reg);
- offset += 4;
- size -= 4;
- }
-
- while (size >= 2) {
- MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, tmp_reg, srcreg, offset);
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, tmp_reg);
- offset += 2;
- size -= 2;
- }
-
- while (size >= 1) {
- MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, tmp_reg, srcreg, offset);
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, tmp_reg);
- offset += 1;
- size -= 1;
- }
-
- return TRUE;
-}
-
-/*
- * Emit code to copy a valuetype of type @klass whose address is stored in
- * @src->dreg to memory whose address is stored at @dest->dreg.
- */
-void
-mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native)
-{
- MonoInst *iargs [4];
- int n;
- guint32 align = 0;
- MonoMethod *memcpy_method;
- MonoInst *size_ins = NULL;
- MonoInst *memcpy_ins = NULL;
-
- g_assert (klass);
- if (cfg->gshared)
- klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
-
- /*
- * This check breaks with spilled vars... need to handle it during verification anyway.
- * g_assert (klass && klass == src->klass && klass == dest->klass);
- */
-
- if (mini_is_gsharedvt_klass (klass)) {
- g_assert (!native);
- size_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
- memcpy_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_MEMCPY);
- }
-
- if (native)
- n = mono_class_native_size (klass, &align);
- else
- n = mono_class_value_size (klass, &align);
-
- if (!align)
- align = SIZEOF_VOID_P;
- /* if native is true there should be no references in the struct */
- if (cfg->gen_write_barriers && (klass->has_references || size_ins) && !native) {
- /* Avoid barriers when storing to the stack */
- if (!((dest->opcode == OP_ADD_IMM && dest->sreg1 == cfg->frame_reg) ||
- (dest->opcode == OP_LDADDR))) {
- int context_used;
-
- iargs [0] = dest;
- iargs [1] = src;
-
- context_used = mini_class_check_context_used (cfg, klass);
-
- /* It's ok to intrinsify under gsharing since shared code types are layout stable. */
- if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && mini_emit_wb_aware_memcpy (cfg, klass, iargs, n, align)) {
- return;
- } else if (size_ins || align < SIZEOF_VOID_P) {
- if (context_used) {
- iargs [2] = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
- } else {
- iargs [2] = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
- if (!cfg->compile_aot)
- mono_class_compute_gc_descriptor (klass);
- }
- if (size_ins)
- mono_emit_jit_icall (cfg, mono_gsharedvt_value_copy, iargs);
- else
- mono_emit_jit_icall (cfg, mono_value_copy, iargs);
- } else {
- /* We don't unroll more than 5 stores to avoid code bloat. */
- /*This is harmless and simplify mono_gc_get_range_copy_func */
- n += (SIZEOF_VOID_P - 1);
- n &= ~(SIZEOF_VOID_P - 1);
-
- EMIT_NEW_ICONST (cfg, iargs [2], n);
- mono_emit_jit_icall (cfg, mono_gc_get_range_copy_func (), iargs);
- }
- }
- }
-
- if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 8) {
- /* FIXME: Optimize the case when src/dest is OP_LDADDR */
- mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, n, align);
- } else {
- iargs [0] = dest;
- iargs [1] = src;
- if (size_ins)
- iargs [2] = size_ins;
- else
- EMIT_NEW_ICONST (cfg, iargs [2], n);
-
- memcpy_method = mini_get_memcpy_method ();
- if (memcpy_ins)
- mini_emit_calli (cfg, mono_method_signature (memcpy_method), iargs, memcpy_ins, NULL, NULL);
- else
- mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
- }
-}
-
MonoMethod*
mini_get_memset_method (void)
{
return addr;
}
-static MonoBreakPolicy
-always_insert_breakpoint (MonoMethod *method)
-{
- return MONO_BREAK_POLICY_ALWAYS;
-}
-
-static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
-
-/**
- * mono_set_break_policy:
- * \param policy_callback the new callback function
- *
- * Allow embedders to decide wherther to actually obey breakpoint instructions
- * (both break IL instructions and \c Debugger.Break method calls), for example
- * to not allow an app to be aborted by a perfectly valid IL opcode when executing
- * untrusted or semi-trusted code.
- *
- * \p policy_callback will be called every time a break point instruction needs to
- * be inserted with the method argument being the method that calls \c Debugger.Break
- * or has the IL \c break instruction. The callback should return \c MONO_BREAK_POLICY_NEVER
- * if it wants the breakpoint to not be effective in the given method.
- * \c MONO_BREAK_POLICY_ALWAYS is the default.
- */
-void
-mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
-{
- if (policy_callback)
- break_policy_func = policy_callback;
- else
- break_policy_func = always_insert_breakpoint;
-}
-
-static gboolean
-should_insert_brekpoint (MonoMethod *method) {
- switch (break_policy_func (method)) {
- case MONO_BREAK_POLICY_ALWAYS:
- return TRUE;
- case MONO_BREAK_POLICY_NEVER:
- return FALSE;
- case MONO_BREAK_POLICY_ON_DBG:
- g_warning ("mdb no longer supported");
- return FALSE;
- default:
- g_warning ("Incorrect value returned from break policy callback");
- return FALSE;
- }
-}
-
/* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic icalls */
static MonoInst*
emit_array_generic_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
return NULL;
}
+
+static gboolean
+mono_type_is_native_blittable (MonoType *t)
+{
+ if (MONO_TYPE_IS_REFERENCE (t))
+ return FALSE;
+
+ if (MONO_TYPE_IS_PRIMITIVE_SCALAR (t))
+ return TRUE;
+
+ MonoClass *klass = mono_class_from_mono_type (t);
+
+ //MonoClass::blitable depends on mono_class_setup_fields being done.
+ mono_class_setup_fields (klass);
+ if (!klass->blittable)
+ return FALSE;
+
+ // If the native marshal size is different we can't convert PtrToStructure to a type load
+ if (mono_class_native_size (klass, NULL) != mono_class_value_size (klass, NULL))
+ return FALSE;
+
+ return TRUE;
+}
+
+
static MonoInst*
mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
{
(strcmp (cmethod->klass->name_space, "System.Diagnostics") == 0) &&
(strcmp (cmethod->klass->name, "Debugger") == 0)) {
if (!strcmp (cmethod->name, "Break") && fsig->param_count == 0) {
- if (should_insert_brekpoint (cfg->method)) {
+ if (mini_should_insert_breakpoint (cfg->method)) {
ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
} else {
MONO_INST_NEW (cfg, ins, OP_NOP);
MONO_ADD_INS (cfg->cbb, ins);
return ins;
}
+ } else if (cmethod->klass->image == mono_defaults.corlib &&
+ (strcmp (cmethod->klass->name_space, "System.Runtime.InteropServices") == 0) &&
+ (strcmp (cmethod->klass->name, "Marshal") == 0)) {
+ //Convert Marshal.PtrToStructure<T> of blittable T to direct loads
+ if (strcmp (cmethod->name, "PtrToStructure") == 0 &&
+ cmethod->is_inflated &&
+ fsig->param_count == 1 &&
+ !mini_method_check_context_used (cfg, cmethod)) {
+
+ MonoGenericContext *method_context = mono_method_get_context (cmethod);
+ MonoType *arg0 = method_context->method_inst->type_argv [0];
+ if (mono_type_is_native_blittable (arg0))
+ return mini_emit_memory_load (cfg, arg0, args [0], 0, 0);
+ }
}
#ifdef MONO_ARCH_SIMD_INTRINSICS
MONO_ADD_INS (cfg->cbb, ins);
break;
case CEE_BREAK:
- if (should_insert_brekpoint (cfg->method)) {
+ if (mini_should_insert_breakpoint (cfg->method)) {
ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
} else {
MONO_INST_NEW (cfg, ins, OP_NOP);
klass = mini_get_class (method, token, generic_context);
CHECK_TYPELOAD (klass);
sp -= 2;
- if (generic_class_is_reference_type (cfg, klass)) {
- MonoInst *store, *load;
- int dreg = alloc_ireg_ref (cfg);
-
- NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, dreg, sp [1]->dreg, 0);
- load->flags |= ins_flag;
- MONO_ADD_INS (cfg->cbb, load);
-
- NEW_STORE_MEMBASE (cfg, store, OP_STORE_MEMBASE_REG, sp [0]->dreg, 0, dreg);
- store->flags |= ins_flag;
- MONO_ADD_INS (cfg->cbb, store);
-
- if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER)
- mini_emit_write_barrier (cfg, sp [0], sp [1]);
- } else {
- mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
- }
+ mini_emit_memory_copy (cfg, sp [0], sp [1], klass, FALSE, ins_flag);
ins_flag = 0;
ip += 5;
break;
}
/* Optimize the ldobj+stobj combination */
- /* The reference case ends up being a load+store anyway */
- /* Skip this if the operation is volatile. */
- if (((ip [5] == CEE_STOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 5) && read32 (ip + 6) == token) && !generic_class_is_reference_type (cfg, klass) && !(ins_flag & MONO_INST_VOLATILE)) {
+ if (((ip [5] == CEE_STOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 5) && read32 (ip + 6) == token)) {
CHECK_STACK (1);
sp --;
- mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
+ mini_emit_memory_copy (cfg, sp [0], sp [1], klass, FALSE, ins_flag);
ip += 5 + 5;
ins_flag = 0;
dreg = alloc_ireg_mp (cfg);
EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
wbarrier_ptr_ins = ins;
- /* The decomposition will call mini_emit_stobj () which will emit a wbarrier if needed */
+ /* The decomposition will call mini_emit_memory_copy () which will emit a wbarrier if needed */
EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, dreg, 0, sp [1]->dreg);
} else {
EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg);
temp = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
temp->backend.is_pinvoke = 1;
EMIT_NEW_TEMPLOADA (cfg, dest, temp->inst_c0);
- mini_emit_stobj (cfg, dest, src, klass, TRUE);
+ mini_emit_memory_copy (cfg, dest, src, klass, TRUE, 0);
EMIT_NEW_TEMPLOAD (cfg, dest, temp->inst_c0);
dest->type = STACK_VTYPE;
} else {
EMIT_NEW_RETLOADA (cfg, ins);
}
- mini_emit_stobj (cfg, ins, sp [0], klass, TRUE);
+ mini_emit_memory_copy (cfg, ins, sp [0], klass, TRUE, 0);
if (sp != stack_start)
UNVERIFIED;
the values on the stack before emitting the last instruction of the bb.
*/
-#else /* !DISABLE_JIT */
-
-void
-mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
-{
-}
-
#endif /* !DISABLE_JIT */
return code;
}
-#if defined(__ARM_EABI__) && defined(__linux__) && !defined(PLATFORM_ANDROID) && !defined(__native_client__)
+#if defined(__ARM_EABI__) && defined(__linux__) && !defined(PLATFORM_ANDROID) && !defined(__native_client__) && !defined(MONO_CROSS_COMPILE)
#define HAVE_AEABI_READ_TP 1
#endif
#ifdef HAVE_AEABI_READ_TP
static gboolean have_fast_tls = FALSE;
static gboolean inited = FALSE;
- gpointer tp1, tp2;
if (mini_get_debug_options ()->use_fallback_tls)
return FALSE;
if (inited)
return have_fast_tls;
- tp1 = __aeabi_read_tp ();
- asm volatile("mrc p15, 0, %0, c13, c0, 3" : "=r" (tp2));
+ if (v7_supported) {
+ gpointer tp1, tp2;
- have_fast_tls = tp1 && tp1 == tp2;
+ tp1 = __aeabi_read_tp ();
+ asm volatile("mrc p15, 0, %0, c13, c0, 3" : "=r" (tp2));
+
+ have_fast_tls = tp1 && tp1 == tp2;
+ }
inited = TRUE;
return have_fast_tls;
#else
static guint8*
emit_tls_get (guint8 *code, int dreg, int tls_offset)
{
+ g_assert (v7_supported);
ARM_MRC (code, 15, 0, dreg, 13, 0, 3);
ARM_LDR_IMM (code, dreg, dreg, tls_offset);
return code;
emit_tls_set (guint8 *code, int sreg, int tls_offset)
{
int tp_reg = (sreg != ARMREG_R0) ? ARMREG_R0 : ARMREG_R1;
+ g_assert (v7_supported);
ARM_MRC (code, 15, 0, tp_reg, 13, 0, 3);
ARM_STR_IMM (code, sreg, tp_reg, tls_offset);
return code;
mgreg_t *new_reg_locations [MONO_MAX_IREGS];
gboolean get_reg_locations = unwind_options & MONO_UNWIND_REG_LOCATIONS;
gboolean async = mono_thread_info_is_async_context ();
+ Unwinder unwinder;
if (mono_llvm_only) {
GSList *l, *ips;
memcpy (&ctx, start_ctx, sizeof (MonoContext));
memset (reg_locations, 0, sizeof (reg_locations));
+ unwinder_init (&unwinder);
+
while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) {
frame.lmf = lmf;
- res = mono_find_jit_info_ext (domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, get_reg_locations ? new_reg_locations : NULL, &frame);
+ res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, get_reg_locations ? new_reg_locations : NULL, &frame);
if (!res)
return;
gpointer ip;
if (in_interp)
- ip = (guint16*)ji->code_start + frame.native_offset;
+ ip = (guint8*)ji->code_start + frame.native_offset;
else
ip = MONO_CONTEXT_GET_IP (ctx);
}
if (in_interp)
- ip = (guint16*)ji->code_start + frame.native_offset;
+ ip = (guint8*)ji->code_start + frame.native_offset;
else
ip = MONO_CONTEXT_GET_IP (ctx);
* like the call which transitioned to JITted code has succeeded, but the
* return value register etc. is not set, so we have to be careful.
*/
- mono_interp_set_resume_state (mono_ex, &frame, ei->handler_start);
+ mono_interp_set_resume_state (jit_tls, mono_ex, frame.interp_frame, ei->handler_start);
/* Undo the IP adjustment done by mono_arch_unwind_frame () */
#if defined(TARGET_AMD64)
ctx->gregs [AMD64_RIP] ++;
* mono_handle_exception:
* \param ctx saved processor state
* \param obj the exception object
+ *
+ * Handle the exception OBJ starting from the state CTX. Modify CTX to point to the handler clause if the exception is caught, and
+ * return TRUE.
*/
gboolean
mono_handle_exception (MonoContext *ctx, MonoObject *obj)
g_assert_not_reached ();
}
#endif
-
-#ifndef ENABLE_INTERPRETER
-/* Stubs of interpreter functions */
-void
-mono_interp_set_resume_state (MonoException *ex, StackFrameInfo *frame, gpointer handler_ip)
-{
- g_assert_not_reached ();
-}
-
-void
-mono_interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip)
-{
- g_assert_not_reached ();
-}
-
-void
-mono_interp_frame_iter_init (MonoInterpStackIter *iter, gpointer interp_exit_data)
-{
- g_assert_not_reached ();
-}
-
-gboolean
-mono_interp_frame_iter_next (MonoInterpStackIter *iter, StackFrameInfo *frame)
-{
- g_assert_not_reached ();
- return FALSE;
-}
-#endif
G_GNUC_UNUSED gboolean
mono_debug_count (void)
{
- static int count = 0;
+ static int count = 0, int_val = 0;
static gboolean inited;
- static char *value;
count ++;
if (!inited) {
- value = g_getenv ("COUNT");
+ char *value = g_getenv ("COUNT");
+ if (value) {
+ int_val = atoi (value);
+ g_free (value);
+ }
inited = TRUE;
}
- if (!value)
+ if (!int_val)
return TRUE;
- int int_val = atoi (value);
- g_free (value);
-
if (count == int_val)
break_count ();
g_assert_not_reached ();
}
+
+static MonoBreakPolicy
+always_insert_breakpoint (MonoMethod *method)
+{
+ return MONO_BREAK_POLICY_ALWAYS;
+}
+
+static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
+
+/**
+ * mono_set_break_policy:
+ * \param policy_callback the new callback function
+ *
+ * Allow embedders to decide whether to actually obey breakpoint instructions
+ * (both break IL instructions and \c Debugger.Break method calls), for example
+ * to not allow an app to be aborted by a perfectly valid IL opcode when executing
+ * untrusted or semi-trusted code.
+ *
+ * \p policy_callback will be called every time a break point instruction needs to
+ * be inserted with the method argument being the method that calls \c Debugger.Break
+ * or has the IL \c break instruction. The callback should return \c MONO_BREAK_POLICY_NEVER
+ * if it wants the breakpoint to not be effective in the given method.
+ * \c MONO_BREAK_POLICY_ALWAYS is the default.
+ */
+void
+mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
+{
+ if (policy_callback)
+ break_policy_func = policy_callback;
+ else
+ break_policy_func = always_insert_breakpoint;
+}
+
+gboolean
+mini_should_insert_breakpoint (MonoMethod *method)
+{
+ switch (break_policy_func (method)) {
+ case MONO_BREAK_POLICY_ALWAYS:
+ return TRUE;
+ case MONO_BREAK_POLICY_NEVER:
+ return FALSE;
+ case MONO_BREAK_POLICY_ON_DBG:
+ g_warning ("mdb no longer supported");
+ return FALSE;
+ default:
+ g_warning ("Incorrect value returned from break policy callback");
+ return FALSE;
+ }
+}
+
// Custom handlers currently only implemented by Windows.
#ifndef HOST_WIN32
gboolean
*/
gpointer abort_exc_stack_threshold;
-
/*
* List of methods being JIT'd in the current thread.
*/
int active_jit_methods;
+
+ gpointer interp_context;
} MonoJitTlsData;
/*
MonoInst* mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this_ins);
void mono_create_helper_signatures (void);
MonoInst* mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig, MonoInst **args);
+gboolean mini_should_insert_breakpoint (MonoMethod *method);
gboolean mini_class_is_system_array (MonoClass *klass);
MonoMethodSignature *mono_get_element_address_signature (int arity);
MonoInst* mini_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg);
MonoInst* mini_emit_memory_barrier (MonoCompile *cfg, int kind);
void mini_emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value);
-gboolean mini_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align);
MonoInst* mini_emit_memory_load (MonoCompile *cfg, MonoType *type, MonoInst *src, int offset, int ins_flag);
void mini_emit_memory_store (MonoCompile *cfg, MonoType *type, MonoInst *dest, MonoInst *value, int ins_flag);
void mini_emit_memory_copy_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoInst *size, int ins_flag);
void mini_emit_memory_init_bytes (MonoCompile *cfg, MonoInst *dest, MonoInst *value, MonoInst *size, int ins_flag);
+void mini_emit_memory_copy (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native, int ins_flag);
MonoMethod* mini_get_memcpy_method (void);
MonoMethod* mini_get_memset_method (void);
guint8 *start = NULL, *code, *label_gexits [gregs_num], *label_fexits [fregs_num], *label_leave_tramp [3], *label_is_float_ret;
MonoJumpInfo *ji = NULL;
GSList *unwind_ops = NULL;
- int buf_len, i, framesize = 0, off_methodargs, off_targetaddr;
+ int buf_len, i, framesize, off_methodargs, off_targetaddr;
const int fp_reg = ARMREG_R7;
buf_len = 512 + 1024;
start = code = (guint8 *) mono_global_codeman_reserve (buf_len);
- off_methodargs = framesize;
+ framesize = 5 * sizeof (mgreg_t); /* lr, r4, r8, r6 and plus one */
+
+ off_methodargs = -framesize;
framesize += sizeof (mgreg_t);
- off_targetaddr = framesize;
+ off_targetaddr = -framesize;
framesize += sizeof (mgreg_t);
- framesize = ALIGN_TO (framesize, MONO_ARCH_FRAME_ALIGNMENT);
+ framesize = ALIGN_TO (framesize + 4 * sizeof (mgreg_t), MONO_ARCH_FRAME_ALIGNMENT);
/* allocate space on stack for argument passing */
const int stack_space = ALIGN_TO (((gregs_num - ARMREG_R3) * sizeof (mgreg_t)), MONO_ARCH_FRAME_ALIGNMENT);
- /* use r4, r5 and r6 as scratch registers */
- ARM_PUSH (code, (1 << fp_reg) | (1 << ARMREG_LR) | (1 << ARMREG_R4) | (1 << ARMREG_R5) | (1 << ARMREG_R6));
+ /* iOS ABI */
+ ARM_PUSH (code, (1 << fp_reg) | (1 << ARMREG_LR));
ARM_MOV_REG_REG (code, fp_reg, ARMREG_SP);
+
+ /* use r4, r8 and r6 as scratch registers */
+ ARM_PUSH (code, (1 << ARMREG_R4) | (1 << ARMREG_R8) | (1 << ARMREG_R6));
ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, stack_space + framesize);
/* save InterpMethodArguments* onto stack */
/* load pointer to InterpMethodArguments* into r4 */
ARM_MOV_REG_REG (code, ARMREG_R4, ARMREG_R1);
- /* move flen into r5 */
- ARM_LDR_IMM (code, ARMREG_R5, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, flen));
+ /* move flen into r8 */
+ ARM_LDR_IMM (code, ARMREG_R8, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, flen));
/* load pointer to fargs into r6 */
ARM_LDR_IMM (code, ARMREG_R6, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, fargs));
for (i = 0; i < fregs_num; ++i) {
- ARM_CMP_REG_IMM (code, ARMREG_R5, 0, 0);
+ ARM_CMP_REG_IMM (code, ARMREG_R8, 0, 0);
label_fexits [i] = code;
ARM_B_COND (code, ARMCOND_EQ, 0);
g_assert (i <= ARM_VFP_D7); /* otherwise, need to pass args on stack */
ARM_FLDD (code, i, ARMREG_R6, i * sizeof (double));
- ARM_SUB_REG_IMM8 (code, ARMREG_R5, ARMREG_R5, 1);
+ ARM_SUB_REG_IMM8 (code, ARMREG_R8, ARMREG_R8, 1);
}
for (i = 0; i < fregs_num; i++)
arm_patch (label_fexits [i], code);
- /* move ilen into r5 */
- ARM_LDR_IMM (code, ARMREG_R5, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, ilen));
+ /* move ilen into r8 */
+ ARM_LDR_IMM (code, ARMREG_R8, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, ilen));
/* load pointer to iargs into r6 */
ARM_LDR_IMM (code, ARMREG_R6, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, iargs));
int stack_offset = 0;
for (i = 0; i < gregs_num; i++) {
- ARM_CMP_REG_IMM (code, ARMREG_R5, 0, 0);
+ ARM_CMP_REG_IMM (code, ARMREG_R8, 0, 0);
label_gexits [i] = code;
ARM_B_COND (code, ARMCOND_EQ, 0);
ARM_STR_IMM (code, ARMREG_R4, ARMREG_SP, stack_offset);
stack_offset += sizeof (mgreg_t);
}
- ARM_SUB_REG_IMM8 (code, ARMREG_R5, ARMREG_R5, 1);
+ ARM_SUB_REG_IMM8 (code, ARMREG_R8, ARMREG_R8, 1);
}
for (i = 0; i < gregs_num; i++)
ARM_LDR_IMM (code, ARMREG_R4, fp_reg, off_methodargs);
/* load is_float_ret */
- ARM_LDR_IMM (code, ARMREG_R5, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, is_float_ret));
+ ARM_LDR_IMM (code, ARMREG_R8, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, is_float_ret));
/* check if a float return value is expected */
- ARM_CMP_REG_IMM (code, ARMREG_R5, 0, 0);
+ ARM_CMP_REG_IMM (code, ARMREG_R8, 0, 0);
label_is_float_ret = code;
ARM_B_COND (code, ARMCOND_NE, 0);
/* greg return */
/* load retval */
- ARM_LDR_IMM (code, ARMREG_R5, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, retval));
+ ARM_LDR_IMM (code, ARMREG_R8, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, retval));
- ARM_CMP_REG_IMM (code, ARMREG_R5, 0, 0);
+ ARM_CMP_REG_IMM (code, ARMREG_R8, 0, 0);
label_leave_tramp [0] = code;
ARM_B_COND (code, ARMCOND_EQ, 0);
/* store greg result, always write back 64bit */
- ARM_STR_IMM (code, ARMREG_R0, ARMREG_R5, 0);
- ARM_STR_IMM (code, ARMREG_R1, ARMREG_R5, 4);
+ ARM_STR_IMM (code, ARMREG_R0, ARMREG_R8, 0);
+ ARM_STR_IMM (code, ARMREG_R1, ARMREG_R8, 4);
label_leave_tramp [1] = code;
ARM_B_COND (code, ARMCOND_AL, 0);
/* freg return */
arm_patch (label_is_float_ret, code);
/* load retval */
- ARM_LDR_IMM (code, ARMREG_R5, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, retval));
+ ARM_LDR_IMM (code, ARMREG_R8, ARMREG_R4, MONO_STRUCT_OFFSET (InterpMethodArguments, retval));
- ARM_CMP_REG_IMM (code, ARMREG_R5, 0, 0);
+ ARM_CMP_REG_IMM (code, ARMREG_R8, 0, 0);
label_leave_tramp [2] = code;
ARM_B_COND (code, ARMCOND_EQ, 0);
/* store freg result */
- ARM_FSTD (code, ARM_VFP_F0, ARMREG_R5, 0);
+ ARM_FSTD (code, ARM_VFP_F0, ARMREG_R8, 0);
for (i = 0; i < 3; i++)
arm_patch (label_leave_tramp [i], code);
+ ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, stack_space + framesize);
+ ARM_POP (code, (1 << ARMREG_R4) | (1 << ARMREG_R8) | (1 << ARMREG_R6));
ARM_MOV_REG_REG (code, ARMREG_SP, fp_reg);
- ARM_POP (code, (1 << fp_reg) | (1 << ARMREG_PC) | (1 << ARMREG_R4) | (1 << ARMREG_R5) | (1 << ARMREG_R6));
+ ARM_POP (code, (1 << fp_reg) | (1 << ARMREG_PC));
g_assert (code - start < buf_len);
--- /dev/null
+using System;
+using System.Runtime.CompilerServices;
+using Mono;
+
+/*
+ * Regression tests for the mono JIT.
+ *
+ * Each test needs to be of the form:
+ *
+ * static int test_<result>_<name> ();
+ *
+ * where <result> is an integer (the value that needs to be returned by
+ * the method to make it pass.
+ * <name> is a user-displayed name used to identify the test.
+ *
+ * The tests can be driven in two ways:
+ * *) running the program directly: Main() uses reflection to find and invoke
+ * the test methods (this is useful mostly to check that the tests are correct)
+ * *) with the --regression switch of the jit (this is the preferred way since
+ * all the tests will be run with optimizations on and off)
+ *
+ * The reflection logic could be moved to a .dll since we need at least another
+ * regression test file written in IL code to have better control on how
+ * the IL code looks.
+ */
+
+#if __MOBILE__
+namespace UnalignedTests
+{
+#endif
+
+
+class Tests {
+
+#if !__MOBILE__
+ public static int Main (string[] args) {
+ return TestDriver.RunTests (typeof (Tests), args);
+ }
+#endif
+
+
+ public static unsafe int test_0_ldobj_r4 ()
+ {
+ byte *ptr = stackalloc byte [32];
+ float f = (float)123.44f;
+ *(float*)ptr = (float)f;
+
+ int expected = *(int*)ptr;
+
+ Intrinsics.UnalignedStobj<int> (ptr + 1, expected);
+ if (Intrinsics.UnalignedLdobj<float> (ptr + 1) != f)
+ return 1;
+
+ return 0;
+ }
+
+ public static unsafe int test_0_ldobj_r8 ()
+ {
+ byte *ptr = stackalloc byte [32];
+ double f = 34423.44f;
+ *(double*)ptr = (double)f;
+
+ long expected = *(long*)ptr;
+
+ Intrinsics.UnalignedStobj<long> (ptr + 3, expected);
+ if (Intrinsics.UnalignedLdobj<double> (ptr + 3) != f)
+ return 1;
+
+ return 0;
+ }
+
+ public static unsafe int test_0_ldobj ()
+ {
+ byte *ptr = stackalloc byte [20];
+ for (int i = 0; i < 20; ++i)
+ ptr [i] = (byte)i;
+
+
+ if (Intrinsics.UnalignedLdobj<short> (ptr + 0) != 0x0100)
+ return 1;
+
+ if (Intrinsics.UnalignedLdobj<short> (ptr + 1) != 0x0201)
+ return 2;
+
+ if (Intrinsics.UnalignedLdobj<short> (ptr + 2) != 0x0302)
+ return 3;
+
+ if (Intrinsics.UnalignedLdobj<int> (ptr + 1) != 0x04030201)
+ return 4;
+
+ if (Intrinsics.UnalignedLdobj<int> (ptr + 2) != 0x05040302)
+ return 5;
+
+ if (Intrinsics.UnalignedLdobj<long> (ptr + 1) != 0x0807060504030201)
+ return 6;
+
+ if (Intrinsics.UnalignedLdobj<long> (ptr + 6) != 0xD0C0B0A09080706)
+ return 7;
+
+ return 0;
+ }
+
+ public static unsafe int test_0_ldind ()
+ {
+ byte *ptr = stackalloc byte [20];
+ for (int i = 0; i < 20; ++i)
+ ptr [i] = (byte)i;
+
+
+ if (Intrinsics.UnalignedLdInd2 (ptr + 0) != 0x0100)
+ return 1;
+
+ if (Intrinsics.UnalignedLdInd2 (ptr + 1) != 0x0201)
+ return 2;
+
+ if (Intrinsics.UnalignedLdInd2 (ptr + 2) != 0x0302)
+ return 3;
+
+ if (Intrinsics.UnalignedLdInd4 (ptr + 1) != 0x04030201)
+ return 4;
+
+ if (Intrinsics.UnalignedLdInd4 (ptr + 2) != 0x05040302)
+ return 5;
+
+ if (Intrinsics.UnalignedLdInd8 (ptr + 1) != 0x0807060504030201)
+ return 6;
+
+ if (Intrinsics.UnalignedLdInd8 (ptr + 6) != 0xD0C0B0A09080706)
+ return 7;
+
+ return 0;
+ }
+ public static unsafe int test_0_cpobj ()
+ {
+ byte *dest = stackalloc byte [20];
+ byte *src = stackalloc byte [20];
+ for (int i = 0; i < 20; ++i)
+ src [i] = (byte)i;
+
+ Intrinsics.UnalignedCpobj<short> (dest + 0, src + 0);
+ if (dest [0] != src [0] || dest [1] != src [1])
+ return 1;
+
+ Intrinsics.UnalignedCpobj<short> (dest + 1, src + 0);
+ if (dest [1] != src [0] || dest [2] != src [1])
+ return 2;
+
+ Intrinsics.UnalignedCpobj<short> (dest + 0, src + 1);
+ if (dest [0] != src [1] || dest [1] != src [2])
+ return 3;
+
+ Intrinsics.UnalignedCpobj<short> (dest + 1, src + 1);
+ if (dest [1] != src [1] || dest [2] != src [2])
+ return 3;
+
+ Intrinsics.UnalignedCpobj<int> (dest + 3, src);
+ for (int i = 0; i < 4; ++i) {
+ if (dest [i + 3] != src [i])
+ return 4;
+ }
+
+ Intrinsics.UnalignedCpobj<int> (dest + 1, src + 2);
+ for (int i = 0; i < 4; ++i) {
+ if (dest [i + 1] != src [i + 2])
+ return 5;
+ }
+
+ Intrinsics.UnalignedCpobj<long> (dest + 1, src + 2);
+ for (int i = 0; i < 8; ++i) {
+ if (dest [i + 1] != src [i + 2])
+ return 6;
+ }
+
+ Intrinsics.UnalignedCpobj<long> (dest + 7, src + 2);
+ for (int i = 0; i < 8; ++i) {
+ if (dest [i + 7] != src [i + 2])
+ return 7;
+ }
+
+ return 0;
+ }
+
+ public static unsafe int test_0_stobj ()
+ {
+ byte *ptr = stackalloc byte [20];
+
+ Intrinsics.UnalignedStobj <short> (ptr + 0, 0x6688);
+ if (ptr [0] != 0x88 || ptr [1] != 0x66)
+ return 1;
+
+ Intrinsics.UnalignedStobj <short> (ptr + 1, 0x6589);
+ if (ptr [1] != 0x89 || ptr [2] != 0x65)
+ return 2;
+
+ Intrinsics.UnalignedStobj <int> (ptr + 1, 0x60708090);
+ if (ptr [1] != 0x90 || ptr [2] != 0x80 || ptr [3] != 0x70 || ptr [4] != 0x60)
+ return 3;
+
+ Intrinsics.UnalignedStobj <long> (ptr + 1, 0x405060708090);
+ if (ptr [1] != 0x90 || ptr [2] != 0x80 || ptr [3] != 0x70 || ptr [4] != 0x60 || ptr [5] != 0x50 || ptr [6] != 0x40)
+ return 4;
+
+ return 0;
+ }
+
+ public static unsafe int test_0_ldobj_stobj ()
+ {
+ byte *dest = stackalloc byte [20];
+ byte *src = stackalloc byte [20];
+
+ for (int i = 0; i < 20; ++i)
+ src [i] = (byte)i;
+
+ Intrinsics.UnalignedLdobjStObjPair<short> (dest + 0, src + 0);
+ if (dest [0] != src [0] || dest [1] != src [1])
+ return 1;
+
+ Intrinsics.UnalignedLdobjStObjPair<short> (dest + 1, src + 0);
+ if (dest [1] != src [0] || dest [2] != src [1])
+ return 2;
+
+ Intrinsics.UnalignedLdobjStObjPair<short> (dest + 0, src + 1);
+ if (dest [0] != src [1] || dest [1] != src [2])
+ return 3;
+
+ Intrinsics.UnalignedLdobjStObjPair<short> (dest + 1, src + 1);
+ if (dest [1] != src [1] || dest [2] != src [2])
+ return 3;
+
+ Intrinsics.UnalignedLdobjStObjPair<int> (dest + 1, src + 1);
+ if (dest [1] != src [1] || dest [2] != src [2])
+ return 4;
+
+ Intrinsics.UnalignedLdobjStObjPair<long> (dest + 1, src + 1);
+ if (dest [1] != src [1] || dest [2] != src [2])
+ return 5;
+
+
+ return 0;
+ }
+
+
+ public static unsafe int test_0_cpblk ()
+ {
+ byte *dest = stackalloc byte [20];
+ byte *src = stackalloc byte [20];
+ for (int i = 0; i < 20; ++i)
+ src [i] = (byte)i;
+
+
+ Intrinsics.UnalignedCpblk (dest + 0, src + 0, 2);
+ if (dest [0] != src [0] || dest [1] != src [1])
+ return 1;
+
+ Intrinsics.UnalignedCpblk (dest + 1, src + 0, 2);
+ if (dest [1] != src [0] || dest [2] != src [1])
+ return 2;
+
+ Intrinsics.UnalignedCpblk (dest + 0, src + 1, 2);
+ if (dest [0] != src [1] || dest [1] != src [2])
+ return 3;
+
+ Intrinsics.UnalignedCpblk (dest + 1, src + 1, 2);
+ if (dest [1] != src [1] || dest [2] != src [2])
+ return 3;
+
+ Intrinsics.UnalignedCpblk (dest + 1, src + 1, 4);
+ for (int i = 0; i < 4; ++i) {
+ if (dest [i + 1] != src [i + 1])
+ return 4;
+ }
+
+ Intrinsics.UnalignedCpblk (dest + 1, src + 1, 8);
+ for (int i = 0; i < 8; ++i) {
+ if (dest [i + 1] != src [i + 1])
+ return 5;
+ }
+
+ return 0;
+ }
+
+
+ public static unsafe int test_0_initblk ()
+ {
+ byte *ptr = stackalloc byte [20];
+
+ for (int i = 0; i < 20; ++i)
+ ptr [i] = (byte)i;
+
+ Intrinsics.UnalignedInit (ptr, 30, 2);
+ if (ptr [0] != 30 || ptr [1] != 30)
+ return 1;
+
+ Intrinsics.UnalignedInit (ptr + 1, 31, 2);
+ if (ptr[0] != 30 || ptr [1] != 31 || ptr [2] != 31)
+ return 2;
+
+ return 0;
+ }
+}
+
+#if __MOBILE__
+}
+#endif
+
+
+
assemblyresolve_event2.2.cs \
appdomain-unload-callback.cs \
appdomain-unload-doesnot-raise-pending-events.cs \
+ appdomain-unload-asmload.cs \
unload-appdomain-on-shutdown.cs \
bug-47295.cs \
loader.cs \
--- /dev/null
+using System;
+using System.Threading.Tasks;
+
+/* This is a regression test that checks that after an AssemblyLoad event fires
+ * in a domain, the domain can be unloaded. In bug # 56694, a
+ * System.Reflection.Assembly object from the unloaded domain was kept alive
+ * and crashed the GC. */
+namespace AppDomainUnloadAsmLoad
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ // Need some threads in play
+ new Program().Run().Wait();
+ }
+
+ private async Task Run()
+ {
+ var appDomain = AppDomain.CreateDomain("Test subdomain", null, AppDomain.CurrentDomain.SetupInformation);
+ try
+ {
+ var driver = (AppDomainTestDriver)appDomain.CreateInstanceAndUnwrap(typeof(AppDomainTestDriver).Assembly.FullName,
+ typeof(AppDomainTestDriver).FullName);
+ driver.Test();
+ }
+ finally
+ {
+ AppDomain.Unload(appDomain);
+ }
+ }
+ }
+
+ class AppDomainTestDriver : MarshalByRefObject
+ {
+ static AppDomainTestDriver()
+ {
+ // Needs a callback so that the runtime fires the
+ // AssembyLoad event for this domain and materializes a System.Reflection.Assembly
+ AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
+ }
+
+ private static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
+ {
+ }
+
+ internal void Test()
+ {
+ /* this can be any class from any assembly that hasn't
+ * already been loaded into the test domain.
+ * System.Xml.dll is good because all the tests link
+ * against it, but it's not otherwise used by this
+ * domain. */
+ var foo = default(System.Xml.XmlException);
+ }
+ }
+}
if (TestITestDelegate (itest) != 0)
return 174;
+ if (TestIfaceNoIcall (itest as ITestPresSig) != 0)
+ return 201;
+
itest = new TestClass ();
if (TestITest (itest) != 0)
if (TestITest (itest) != 0)
return 176;
+
#endif
#endregion // Runtime Callable Wrapper Tests
void ITestIn ([MarshalAs (UnmanagedType.Interface)]ITest val);
[MethodImplAttribute (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void ITestOut ([MarshalAs (UnmanagedType.Interface)]out ITest val);
+ int Return22NoICall();
}
[ComImport ()]
[MethodImplAttribute (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[PreserveSig ()]
int ITestOut ([MarshalAs (UnmanagedType.Interface)]out ITestPresSig val);
+ [PreserveSig ()]
+ int Return22NoICall();
}
[System.Runtime.InteropServices.GuidAttribute ("00000000-0000-0000-0000-000000000002")]
public virtual extern void ITestIn ([MarshalAs (UnmanagedType.Interface)]ITest val);
[MethodImplAttribute (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void ITestOut ([MarshalAs (UnmanagedType.Interface)]out ITest val);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
+ public virtual extern int Return22NoICall();
}
[System.Runtime.InteropServices.GuidAttribute ("00000000-0000-0000-0000-000000000002")]
val = new ManagedTestPresSig ();
return 0;
}
+
+ public int Return22NoICall()
+ {
+ return 88;
+ }
}
public class ManagedTest : ITest
return new ManagedTest ();
}
}
+
+ public int Return22NoICall()
+ {
+ return 99;
+ }
}
public static int mono_test_marshal_variant_in_callback (VarEnum vt, object obj)
}
return 0;
}
+
+ public static int TestIfaceNoIcall (ITestPresSig itest) {
+ return itest.Return22NoICall () == 22 ? 0 : 1;
+ }
}
public class TestVisible
int (STDCALL *DoubleIn)(MonoComObject* pUnk, double a);
int (STDCALL *ITestIn)(MonoComObject* pUnk, MonoComObject* pUnk2);
int (STDCALL *ITestOut)(MonoComObject* pUnk, MonoComObject* *ppUnk);
+ int (STDCALL *Return22NoICall)(MonoComObject* pUnk);
} MonoIUnknown;
struct MonoComObject
return S_OK;
}
+LIBTEST_API int STDCALL
+Return22NoICall(MonoComObject* pUnk)
+{
+ return 22;
+}
+
+
static void create_com_object (MonoComObject** pOut);
LIBTEST_API int STDCALL
(*pOut)->vtbl->ITestIn = ITestIn;
(*pOut)->vtbl->ITestOut = ITestOut;
(*pOut)->vtbl->get_ITest = get_ITest;
+ (*pOut)->vtbl->Return22NoICall = Return22NoICall;
}
static MonoComObject* same_object = NULL;
return 1;
return 0;
}
+
+ public static int test_0_generic_ptr_to_struct () {
+ int size = Marshal.SizeOf (typeof (SimpleStruct2));
+ IntPtr p = Marshal.AllocHGlobal (size);
+
+ Marshal.WriteInt32 (p, 0, 1); //a
+ Marshal.WriteInt32 (p, 4, 2); //a
+
+ var s = Marshal.PtrToStructure<SimpleStruct2> (p);
+
+ if (s.a != 1)
+ return 1;
+ if (s.b != 2)
+ return 2;
+ return 0;
+ }
}
if !CROSS_COMPILE
if !HOST_WIN32
if SUPPORT_BOEHM
+if SUPPORT_SGEN
noinst_LTLIBRARIES = libtestlib.la
libtestlib_la_SOURCES =
echo "</test-case></results></test-suite></test-results>" >> TestResult-unit-tests.xml; \
fi;
+endif SUPPORT_SGEN
endif SUPPORT_BOEHM
endif !HOST_WIN32
endif !CROSS_COMPILE
mib [2] = KERN_PROC_ALL;
mib [3] = 0;
- res = sysctl (mib, 4, NULL, &data_len, NULL, 0);
+ res = sysctl (mib, 3, NULL, &data_len, NULL, 0);
if (res)
return NULL;
processes = (struct kinfo_proc *) g_malloc (data_len);
- res = sysctl (mib, 4, processes, &data_len, NULL, 0);
+ res = sysctl (mib, 3, processes, &data_len, NULL, 0);
if (res < 0) {
g_free (processes);
if (errno != ENOMEM)
<ClCompile Include="..\mono\mini\mini-native-types.c" />\r
<ClCompile Include="..\mono\mini\type-checking.c" />\r
<ClCompile Include="..\mono\mini\lldb.c" />\r
+ <ClCompile Include="..\mono\mini\interp\interp-stubs.c" />\r
</ItemGroup>\r
<PropertyGroup Label="Globals">\r
<ProjectGuid>{CB0D9E92-293C-439C-9AC7-C5F59B6E0772}</ProjectGuid>\r
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
<ImportGroup Label="ExtensionTargets">\r
</ImportGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r
<ClCompile Include="..\mono\mini\lldb.c">\r
<Filter>Source Files</Filter>\r
</ClCompile>\r
+ <ClCompile Include="..\mono\mini\interp\interp-stubs.c">\r
+ <Filter>Source Files</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="..\mono\mini\abcremoval.h">\r
<Filter>Resource Files</Filter>\r
</None>\r
</ItemGroup>\r
-</Project>
\ No newline at end of file
+</Project>\r