Description: <short summary of the patch>
 TODO: Put a short summary on the line above and replace this paragraph
 with a longer explanation of this change. Complete the meta-information
 with other relevant fields (see below for details). To make it easier, the
 information below has been extracted from the changelog. Adjust it or drop
 it.
 .
 mono (4.2.1.102+dfsg2-7) unstable; urgency=medium
 .
   [ Jo Shields ]
   * [ddd8779] Suggest reference assemblies for mono-devel. Not needed, but
     sometimes useful.
   * [1975195] Use X509 serial number in Mono cert store filename.
     The Mono cert store traditionally uses the SKI for the filename of a
     certificate, but this does not handle cases where a cert has been
     reissued with the same SKI but different serial (seen in the wild
     with StartCom certs). This patch appends the serial to all new
     imported certs, and attempts to interoperate cleanly with existing
     serial-less certificate stores to avoid duplication.
     (Closes: #30902)
   * [4725363] Fix cert store mover (symlinks to directories are -d)
   * [6da2dfa] Prerequisite near-empty commit for arm64 patch to apply
   * [bba162f] Backport 3cd04f97199ff38d7316587e44381638ba469565 to 4.2 branch
   * [2e9629f] Fix edge case in new certdir migration code (Closes: #820082)
   * [1c25daa] Add ARM64 packages - SGen only (Closes: #789771)
   * [664b438] Fix up sgen-only packackaging rules
 .
   [ Rodrigo Kumpera ]
   * [609e2d7] [utils] Fix amd64 version of MONO_CONTEXT_GET_CURRENT.
     (Closes: #818329)
     Registers must be saved in MonoContext in the same order as
     mono_sigctx_to_monoctx.
     This caused crashes on linux when unified suspend was enabled.
     https://bugzilla.xamarin.com/show_bug.cgi?id=33020
     https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=818329
     (cherry picked from upstream commit 23e34d77f2fd23ab5d61f6ba4aa905817f57668a)
 .
   [ Zoltan Varga ]
   * [efc7a57] Implement handler block guard trampolines.
     (cherry picked from commit d93dab827831b94fc65ab9dae5ce531e8ccd7ebe)
   * [9414811] Fix the generation of static rgctx trampolines when using aot
     on linux.
     (cherry picked from commit 455d2b81e2e662569b9654c79b5e1763bf133fa0)
Author: Jo Shields <jo.shields@xamarin.com>
Bug-Debian: https://bugs.debian.org/30902
Bug-Debian: https://bugs.debian.org/789771
Bug-Debian: https://bugs.debian.org/818329
Bug-Debian: https://bugs.debian.org/820082

---
The information above should follow the Patch Tagging Guidelines, please
checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
are templates for supplementary fields that you might want to add:

Origin: <vendor|upstream|other>, <url of original patch>
Bug: <url in upstream bugtracker>
Bug-Debian: https://bugs.debian.org/<bugnumber>
Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
Forwarded: <no|not-needed|url proving that it has been forwarded>
Reviewed-By: <name and email of someone who approved the patch>
Last-Update: <YYYY-MM-DD>

--- mono-4.2.1.102+dfsg2.orig/Makefile.am
+++ mono-4.2.1.102+dfsg2/Makefile.am
@@ -10,7 +10,7 @@ SUBDIRS = po $(libgc_dir) eglib mono $(i
 ## 'tools' is not normally built
 DIST_SUBDIRS = m4 po $(libgc_dir) eglib mono ikvm-native support data runtime scripts man samples tools msvc docs
 else
-SUBDIRS = po $(libgc_dir) eglib mono $(ikvm_native_dir) support data runtime scripts man samples msvc $(docs_dir)
+SUBDIRS = po $(libgc_dir) eglib mono $(ikvm_native_dir) support data runtime scripts man samples msvc
 # Keep in sync with SUBDIRS
 ## 'tools' is not normally built
 DIST_SUBDIRS = m4 po $(libgc_dir) eglib mono ikvm-native support data runtime scripts man samples tools msvc docs
--- mono-4.2.1.102+dfsg2.orig/configure.ac
+++ mono-4.2.1.102+dfsg2/configure.ac
@@ -2732,6 +2732,7 @@ case "$host" in
 	mips*)
 		TARGET=MIPS;
 		arch_target=mips;
+		with_tls=pthread
 		sgen_supported=true
 		ACCESS_UNALIGNED="no"
 
--- mono-4.2.1.102+dfsg2.orig/data/mono-cairo.pc.in
+++ mono-4.2.1.102+dfsg2/data/mono-cairo.pc.in
@@ -1,5 +1,5 @@
-prefix=${pcfiledir}/../..
-exec_prefix=${pcfiledir}/../..
+prefix=@prefix@
+exec_prefix=${prefix}
 libdir=${prefix}/@reloc_libdir@
 includedir=${prefix}/include
 
--- mono-4.2.1.102+dfsg2.orig/data/mono.pc.in
+++ mono-4.2.1.102+dfsg2/data/mono.pc.in
@@ -1,5 +1,5 @@
-prefix=${pcfiledir}/../..
-exec_prefix=${pcfiledir}/../..
+prefix=@prefix@
+exec_prefix=${prefix}
 libdir=${prefix}/@reloc_libdir@
 includedir=${prefix}/include/mono-@API_VER@
 sysconfdir=@sysconfdir@
--- mono-4.2.1.102+dfsg2.orig/data/net_1_1/machine.config
+++ mono-4.2.1.102+dfsg2/data/net_1_1/machine.config
@@ -75,7 +75,7 @@
 			<add prefix="file" type="System.Net.FileWebRequestCreator, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
 		</webRequestModules>
 		<settings>
-			<ipv6 enabled="false"/>
+			<ipv6 enabled="true"/>
 		</settings>
 	</system.net>
 	<system.web>
--- mono-4.2.1.102+dfsg2.orig/data/net_2_0/machine.config
+++ mono-4.2.1.102+dfsg2/data/net_2_0/machine.config
@@ -119,7 +119,7 @@
 			<add prefix="ftp" type="System.Net.FtpRequestCreator, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
 		</webRequestModules>
 		<settings>
-			<ipv6 enabled="false"/>
+			<ipv6 enabled="true"/>
 		</settings>
 	</system.net>
 	
--- mono-4.2.1.102+dfsg2.orig/data/net_4_0/machine.config
+++ mono-4.2.1.102+dfsg2/data/net_4_0/machine.config
@@ -136,7 +136,7 @@
 			<add prefix="ftp" type="System.Net.FtpRequestCreator, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
 		</webRequestModules>
 		<settings>
-			<ipv6 enabled="false"/>
+			<ipv6 enabled="true"/>
 		</settings>
 	</system.net>
 	
--- mono-4.2.1.102+dfsg2.orig/data/net_4_5/machine.config
+++ mono-4.2.1.102+dfsg2/data/net_4_5/machine.config
@@ -139,7 +139,7 @@
 			<add prefix="ftp" type="System.Net.FtpRequestCreator, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
 		</webRequestModules>
 		<settings>
-			<ipv6 enabled="false"/>
+			<ipv6 enabled="true"/>
 		</settings>
 	</system.net>
 	
--- mono-4.2.1.102+dfsg2.orig/libgc/include/private/gcconfig.h
+++ mono-4.2.1.102+dfsg2/libgc/include/private/gcconfig.h
@@ -2157,6 +2157,50 @@
 	extern char * GC_FreeBSDGetDataStart();
 #	define DATASTART GC_FreeBSDGetDataStart(0x1000, &etext)
 #   endif
+#   ifdef FREEBSD
+#	define OS_TYPE "FREEBSD"
+#	ifndef GC_FREEBSD_THREADS
+#	    define MPROTECT_VDB
+#	endif
+#	ifdef __GLIBC__
+#	    define SIG_SUSPEND		(32+6)
+#	    define SIG_THR_RESTART	(32+5)
+	    extern int _end[];
+#	    define DATAEND (_end)
+#	else
+#	    define SIG_SUSPEND SIGUSR1
+#	    define SIG_THR_RESTART SIGUSR2
+#	endif
+#	define FREEBSD_STACKBOTTOM
+#	ifdef __ELF__
+#	    define DYNAMIC_LOADING
+#	endif
+	extern char etext[];
+	extern char * GC_FreeBSDGetDataStart();
+#	define DATASTART GC_FreeBSDGetDataStart(0x1000, &etext)
+#   endif
+#   ifdef FREEBSD
+#	define OS_TYPE "FREEBSD"
+#	ifndef GC_FREEBSD_THREADS
+#	    define MPROTECT_VDB
+#	endif
+#	ifdef __GLIBC__
+#	    define SIG_SUSPEND		(32+6)
+#	    define SIG_THR_RESTART	(32+5)
+	    extern int _end[];
+#	    define DATAEND (_end)
+#	else
+#	    define SIG_SUSPEND SIGUSR1
+#	    define SIG_THR_RESTART SIGUSR2
+#	endif
+#	define FREEBSD_STACKBOTTOM
+#	ifdef __ELF__
+#	    define DYNAMIC_LOADING
+#	endif
+	extern char etext[];
+	extern char * GC_FreeBSDGetDataStart();
+#	define DATASTART GC_FreeBSDGetDataStart(0x1000, &etext)
+#   endif
 #   ifdef NETBSD
 #	define OS_TYPE "NETBSD"
 #	ifdef __ELF__
--- mono-4.2.1.102+dfsg2.orig/mcs/class/Mono.Security/Mono.Security.X509/X509Store.cs
+++ mono-4.2.1.102+dfsg2/mcs/class/Mono.Security/Mono.Security.X509/X509Store.cs
@@ -128,12 +128,25 @@ namespace Mono.Security.X509 {
 
 			string filename = Path.Combine (_storePath, GetUniqueName (certificate));
 			if (!File.Exists (filename)) {
-				using (FileStream fs = File.Create (filename)) {
-					byte[] data = certificate.RawData;
-					fs.Write (data, 0, data.Length);
-					fs.Close ();
+				filename = Path.Combine (_storePath, GetUniqueNameWithSerial (certificate));
+				if (!File.Exists (filename)) {
+					using (FileStream fs = File.Create (filename)) {
+						byte[] data = certificate.RawData;
+						fs.Write (data, 0, data.Length);
+						fs.Close ();
+					}
+					ClearCertificates ();	// We have modified the store on disk.  So forget the old state.
+				}
+			} else {
+				string newfilename = Path.Combine (_storePath, GetUniqueNameWithSerial (certificate));
+				if (GetUniqueNameWithSerial (LoadCertificate (filename)) != GetUniqueNameWithSerial (certificate)) {
+					using (FileStream fs = File.Create (newfilename)) {
+						byte[] data = certificate.RawData;
+						fs.Write (data, 0, data.Length);
+						fs.Close ();
+					}
+					ClearCertificates ();	// We have modified the store on disk.  So forget the old state.
 				}
-				ClearCertificates ();	// We have modified the store on disk.  So forget the old state.
 			}
 #if !NET_2_1
 			// Try to save privateKey if available..
@@ -164,10 +177,16 @@ namespace Mono.Security.X509 {
 
 		public void Remove (X509Certificate certificate) 
 		{
-			string filename = Path.Combine (_storePath, GetUniqueName (certificate));
+			string filename = Path.Combine (_storePath, GetUniqueNameWithSerial (certificate));
 			if (File.Exists (filename)) {
 				File.Delete (filename);
 				ClearCertificates ();	// We have modified the store on disk.  So forget the old state.
+			} else {
+				filename = Path.Combine (_storePath, GetUniqueName (certificate));
+				if (File.Exists (filename)) {
+					File.Delete (filename);
+					ClearCertificates ();	// We have modified the store on disk.  So forget the old state.
+				}
 			}
 		}
 
@@ -182,10 +201,15 @@ namespace Mono.Security.X509 {
 
 		// private stuff
 
-		private string GetUniqueName (X509Certificate certificate) 
+		private string GetUniqueNameWithSerial (X509Certificate certificate)
+		{
+			return GetUniqueName (certificate, certificate.SerialNumber);
+		}
+
+		private string GetUniqueName (X509Certificate certificate, byte[] serial = null) 
 		{
 			string method;
-			byte[] name = GetUniqueName (certificate.Extensions);
+			byte[] name = GetUniqueName (certificate.Extensions, serial);
 			if (name == null) {
 				method = "tbp"; // thumbprint
 				name = certificate.Hash;
@@ -208,7 +232,7 @@ namespace Mono.Security.X509 {
 			return GetUniqueName (method, name, ".crl");
 		}
 
-		private byte[] GetUniqueName (X509ExtensionCollection extensions) 
+		private byte[] GetUniqueName (X509ExtensionCollection extensions, byte[] serial = null) 
 		{
 			// We prefer Subject Key Identifier as the unique name
 			// as it will provide faster lookups
@@ -217,7 +241,14 @@ namespace Mono.Security.X509 {
 				return null;
 
 			SubjectKeyIdentifierExtension ski = new SubjectKeyIdentifierExtension (ext);
-			return ski.Identifier;
+			if (serial == null) {
+				return ski.Identifier;
+			} else {
+				byte[] uniqueWithSerial = new byte[ski.Identifier.Length + serial.Length];
+				System.Buffer.BlockCopy (ski.Identifier, 0, uniqueWithSerial, 0, ski.Identifier.Length );
+				System.Buffer.BlockCopy (serial, 0, uniqueWithSerial, ski.Identifier.Length, serial.Length );
+				return uniqueWithSerial;
+			}
 		}
 
 		private string GetUniqueName (string method, byte[] name, string fileExtension) 
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/Mono.WebBrowser/Mono.NullBrowser/Base.cs
@@ -0,0 +1,174 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2007, 2008 Novell, Inc.
+//
+// Authors:
+//	Andreia Gaita (avidigal@novell.com)
+//
+
+using System;
+using System.Text;
+using System.Collections;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using Mono.WebBrowser;
+
+namespace Mono.NullBrowser
+{
+	internal class Base
+	{
+		private static Hashtable boundControls;
+		private static bool initialized;
+		private static object initLock = new object ();
+		private static string monoMozDir;
+
+		private static bool isInitialized ()
+		{
+			if (!initialized)
+				return false;
+			return true;
+		}
+
+		static Base ()
+		{
+			boundControls = new Hashtable ();
+		}
+
+		public Base () { }
+
+		public static bool Init (WebBrowser control, Platform platform)
+		{
+			lock (initLock) {
+				if (!initialized) {
+				
+					initialized = true;
+				}
+			}
+			return initialized;
+		}
+
+		public static bool Bind (WebBrowser control, IntPtr handle, int width, int height)
+		{
+			if (!isInitialized ())
+				return false;
+
+			
+			return true;
+		}
+
+		public static bool Create (IWebBrowser control) {
+			if (!isInitialized ())
+				return false;
+
+			return true;
+		}
+
+		public static void Shutdown (IWebBrowser control)
+		{
+			lock (initLock) {
+				if (!initialized)
+					return;
+					
+				
+				boundControls.Remove (control);
+				if (boundControls.Count == 0) {
+					initialized = false;
+				}
+			}
+		}
+
+		// layout
+		public static void Focus (IWebBrowser control, FocusOption focus)
+		{
+			if (!isInitialized ())
+				return;
+
+		}
+
+
+		public static void Blur (IWebBrowser control)
+		{
+			if (!isInitialized ())
+				return;
+
+		}
+
+		public static void Activate (IWebBrowser control)
+		{
+			if (!isInitialized ())
+				return;
+
+		}
+
+		public static void Deactivate (IWebBrowser control)
+		{
+			if (!isInitialized ())
+				return;
+
+		}
+
+		public static void Resize (IWebBrowser control, int width, int height)
+		{
+			if (!isInitialized ())
+				return;
+
+		}
+
+		// navigation
+		public static void Home (IWebBrowser control)
+		{
+			if (!isInitialized ())
+				return;
+
+		}
+
+
+		public static IntPtr StringInit ()
+		{
+			return IntPtr.Zero;
+		}
+
+		public static void StringFinish (HandleRef str)
+		{
+		}
+
+		public static string StringGet (HandleRef str)
+		{
+			return String.Empty;
+		}
+
+		public static void StringSet (HandleRef str, string text)
+		{
+		}
+
+
+		public static object GetProxyForObject (IWebBrowser control, Guid iid, object obj)
+		{
+			return null;
+		}
+
+		public static string EvalScript (IWebBrowser control, string script)
+		{
+			return null;
+		}
+
+
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/Mono.WebBrowser/Mono.NullBrowser/DOM/Navigation.cs
@@ -0,0 +1,127 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2007, 2008 Novell, Inc.
+//
+// Authors:
+//	Andreia Gaita (avidigal@novell.com)
+//
+
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using Mono.WebBrowser;
+using Mono.WebBrowser.DOM;
+
+namespace Mono.NullBrowser.DOM
+{
+	internal class Navigation: INavigation
+	{
+
+
+		#region INavigation Members
+
+		public bool CanGoBack {
+			get {
+				return false;
+			}
+		}
+
+		public bool CanGoForward {
+			get {
+				return false;
+			}
+		}
+
+		public bool Back ()
+		{
+			return false;
+		}
+
+		public bool Forward ()
+		{
+			return false;
+		}
+
+		public void Home ()
+		{
+		}
+
+		public void Reload ()
+		{
+		}
+
+		public void Reload (ReloadOption option)
+		{
+		}
+
+		public void Stop ()
+		{
+		}
+		
+		
+		/// <summary>
+		/// Navigate to the page in the history, by index.
+		/// </summary>
+		/// <param name="index">
+		/// A <see cref="System.Int32"/> representing an absolute index in the 
+		/// history (that is, > -1 and < history length
+		/// </param>
+		public void Go (int index)
+		{
+		}
+
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="index">
+		/// A <see cref="System.Int32"/> representing an index in the 
+		/// history, that can be relative or absolute depending on the relative argument
+		/// </param>
+		/// <param name="relative">
+		/// A <see cref="System.Boolean"/> indicating whether the index is relative to 
+		/// the current place in history or not (i.e., if relative = true, index can be
+		/// positive or negative, and index=-1 means load the previous page in the history.
+		/// if relative = false, index must be > -1, and index = 0 means load the first
+		/// page of the history.
+		/// </param>
+		public void Go (int index, bool relative) {
+		}
+		
+		public void Go (string url)
+		{
+		}
+
+		public void Go (string url, LoadFlags flags) 
+		{
+		}
+
+		public int HistoryCount {
+			get {
+				return 0;
+			}
+		}
+
+		#endregion
+
+		public override int GetHashCode () {
+			return 0;
+		}		
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/Mono.WebBrowser/Mono.NullBrowser/WebBrowser.cs
@@ -0,0 +1,414 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2007, 2008 Novell, Inc.
+// Copyright (c) 2013 Jo Shields
+//
+// Authors:
+//	Andreia Gaita (avidigal@novell.com)
+//	Jo Shields (directhex@apebox.org)
+//
+
+#undef debug
+
+using System;
+using System.Collections;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Diagnostics;
+using Mono.WebBrowser;
+using Mono.WebBrowser.DOM;
+
+namespace Mono.NullBrowser
+{
+	internal class WebBrowser : IWebBrowser
+	{
+		bool loaded;
+		internal bool created = false;
+		bool creating = false;
+
+                internal DOM.Navigation navigation;
+
+		internal Platform platform;
+		internal Platform enginePlatform;
+		System.ComponentModel.EventHandlerList events;
+		System.ComponentModel.EventHandlerList domEvents;
+
+		string statusText;
+
+		bool streamingMode;
+		
+		internal Hashtable documents;
+
+		int width;
+		int height;
+		bool isDirty;
+		
+		public WebBrowser (Platform platform)
+		{
+			this.platform = platform;
+			loaded = Base.Init (this, platform);
+			documents = new Hashtable ();
+		}
+
+		public bool Load (IntPtr handle, int width, int height)
+		{
+			loaded = Base.Bind (this, handle, width, height);
+			return loaded;
+		}
+
+		bool Created {
+			get {
+				if (!creating && !created) {
+					creating = true;
+					created = Base.Create (this);
+					if (created && isDirty) {
+						isDirty = false;
+						Base.Resize (this, width, height);
+					}
+				}
+				return created;
+			}
+		}
+
+		public void Shutdown ()
+		{
+			Base.Shutdown (this);
+		}
+		
+		internal void Reset ()
+		{
+			this.DomEvents.Dispose ();
+			this.domEvents = null;
+			this.documents.Clear ();
+		}
+
+		public bool Initialized {
+			get { return this.loaded; }
+		}
+		
+		public IWindow Window {
+			get {
+				return null;
+			}
+		}
+
+		public IDocument Document {
+			get {
+				return null;
+			}
+		}
+
+		public INavigation Navigation {
+			get {
+                                if (!Created) return null;
+
+                                if (navigation == null) {
+
+                                        navigation = new DOM.Navigation ();
+                                }
+                                return navigation as INavigation;
+			}
+		}
+		
+		public string StatusText {
+			get { return statusText; }
+		}
+		
+		public bool Offline {
+			get {
+				return false;
+			}
+			set {
+			}
+		}
+		
+		internal System.ComponentModel.EventHandlerList DomEvents {
+			get {
+				if (domEvents == null)
+					domEvents = new System.ComponentModel.EventHandlerList();
+
+				return domEvents;
+			}
+		}
+
+		internal System.ComponentModel.EventHandlerList Events {
+			get {
+				if (events == null)
+					events = new System.ComponentModel.EventHandlerList();
+
+				return events;
+			}
+		}
+		
+		#region Layout
+		public void FocusIn (FocusOption focus)
+		{
+			if (!created) return;
+			Base.Focus (this, focus);
+		}
+		public void FocusOut ()
+		{
+			if (!created) return;
+			Base.Blur (this);
+		}
+		
+		public void Activate ()
+		{
+			if (!Created) return;
+			Base.Activate (this);
+		}
+		public void Deactivate ()
+		{
+			if (!created) return;
+			Base.Deactivate (this);
+		}
+
+		public void Resize (int width, int height)
+		{
+			this.width = width;
+			this.height = height;
+			isDirty = true;
+			if (!created) return;
+			Base.Resize (this, width, height);			
+		}
+
+		public void Render (byte[] data)
+		{
+			if (!Created) return;
+			if (data == null)
+				throw new ArgumentNullException ("data");
+			string html = System.Text.ASCIIEncoding.UTF8.GetString (data);
+			Render (html);
+		}
+
+		public void Render (string html)
+		{
+			if (!Created) return;
+			Render (html, "file:///", "text/html");
+		}
+
+				
+		public void Render (string html, string uri, string contentType)
+		{
+			if (!Created) return;
+			throw new Mono.WebBrowser.Exception (Mono.WebBrowser.Exception.ErrorCodes.Navigation);
+
+			IntPtr native_html = Marshal.StringToHGlobalAnsi (html);
+			Marshal.FreeHGlobal (native_html);
+
+
+		}
+		
+		public void ExecuteScript (string script) {
+			if (!Created) return;
+			Base.EvalScript (this, script);
+		}
+				
+		internal void AttachEvent (INode node, string eve, EventHandler handler) {
+			string key = String.Intern (node.GetHashCode() + ":" + eve);
+#if debug
+			Console.Error.WriteLine ("Event Attached: " + key);
+#endif
+			DomEvents.AddHandler (key, handler);
+		}
+
+		internal void DetachEvent (INode node, string eve, EventHandler handler) {
+			string key = String.Intern (node.GetHashCode() + ":" + eve);
+#if debug			
+			Console.Error.WriteLine ("Event Detached: " + key);
+#endif			
+			DomEvents.RemoveHandler (key, handler);
+		}
+		
+		#endregion
+
+		#region Events
+		internal static object KeyDownEvent = new object ();
+		internal static object KeyPressEvent = new object ();
+		internal static object KeyUpEvent = new object ();
+		internal static object MouseClickEvent = new object ();
+		internal static object MouseDoubleClickEvent = new object ();
+		internal static object MouseDownEvent = new object ();
+		internal static object MouseEnterEvent = new object ();
+		internal static object MouseLeaveEvent = new object ();
+		internal static object MouseMoveEvent = new object ();
+		internal static object MouseUpEvent = new object ();
+		internal static object FocusEvent = new object ();
+		internal static object BlurEvent = new object ();
+		internal static object CreateNewWindowEvent = new object ();
+		internal static object AlertEvent = new object ();
+			
+		internal static object LoadStartedEvent = new object ();
+		internal static object LoadCommitedEvent = new object ();
+		internal static object ProgressChangedEvent = new object ();
+		internal static object LoadFinishedEvent = new object ();	
+		
+		internal static object LoadEvent = new object ();
+		internal static object UnloadEvent = new object ();
+		internal static object StatusChangedEvent = new object ();
+		internal static object SecurityChangedEvent = new object ();
+		internal static object ProgressEvent = new object ();
+		internal static object ContextMenuEvent = new object ();
+		
+		internal static object NavigationRequestedEvent = new object ();
+		
+		public event NodeEventHandler KeyDown
+		{
+			add { Events.AddHandler (KeyDownEvent, value); }
+			remove { Events.RemoveHandler (KeyDownEvent, value); }
+		}
+
+		public event NodeEventHandler KeyPress
+		{
+			add { Events.AddHandler (KeyPressEvent, value); }
+			remove { Events.RemoveHandler (KeyPressEvent, value); }
+		}
+		public event NodeEventHandler KeyUp
+		{
+			add { Events.AddHandler (KeyUpEvent, value); }
+			remove { Events.RemoveHandler (KeyUpEvent, value); }
+		}
+		public event NodeEventHandler MouseClick
+		{
+			add { Events.AddHandler (MouseClickEvent, value); }
+			remove { Events.RemoveHandler (MouseClickEvent, value); }
+		}
+		public event NodeEventHandler MouseDoubleClick
+		{
+			add { Events.AddHandler (MouseDoubleClickEvent, value); }
+			remove { Events.RemoveHandler (MouseDoubleClickEvent, value); }
+		}
+		public event NodeEventHandler MouseDown
+		{
+			add { Events.AddHandler (MouseDownEvent, value); }
+			remove { Events.RemoveHandler (MouseDownEvent, value); }
+		}
+		public event NodeEventHandler MouseEnter
+		{
+			add { Events.AddHandler (MouseEnterEvent, value); }
+			remove { Events.RemoveHandler (MouseEnterEvent, value); }
+		}
+		public event NodeEventHandler MouseLeave
+		{
+			add { Events.AddHandler (MouseLeaveEvent, value); }
+			remove { Events.RemoveHandler (MouseLeaveEvent, value); }
+		}
+		public event NodeEventHandler MouseMove
+		{
+			add { Events.AddHandler (MouseMoveEvent, value); }
+			remove { Events.RemoveHandler (MouseMoveEvent, value); }
+		}
+		public event NodeEventHandler MouseUp
+		{
+			add { Events.AddHandler (MouseUpEvent, value); }
+			remove { Events.RemoveHandler (MouseUpEvent, value); }
+		}
+		public event EventHandler Focus
+		{
+			add { Events.AddHandler (FocusEvent, value); }
+			remove { Events.RemoveHandler (FocusEvent, value); }
+		}
+		public event EventHandler Blur
+		{
+			add { Events.AddHandler (BlurEvent, value); }
+			remove { Events.RemoveHandler (BlurEvent, value); }
+		}
+		public event CreateNewWindowEventHandler CreateNewWindow
+		{
+			add { Events.AddHandler (CreateNewWindowEvent, value); }
+			remove { Events.RemoveHandler (CreateNewWindowEvent, value); }
+		}
+		public event AlertEventHandler Alert
+		{
+			add { Events.AddHandler (AlertEvent, value); }
+			remove { Events.RemoveHandler (AlertEvent, value); }
+		}
+		public event EventHandler Loaded
+		{
+			add { Events.AddHandler (LoadEvent, value); }
+			remove { Events.RemoveHandler (LoadEvent, value); }
+		}
+		public event EventHandler Unloaded
+		{
+			add { Events.AddHandler (UnloadEvent, value); }
+			remove { Events.RemoveHandler (UnloadEvent, value); }
+		}
+
+ 		public event StatusChangedEventHandler StatusChanged
+		{
+			add { Events.AddHandler (StatusChangedEvent, value); }
+			remove { Events.RemoveHandler (StatusChangedEvent, value); }
+		}
+		
+ 		public event SecurityChangedEventHandler SecurityChanged
+		{
+			add { Events.AddHandler (SecurityChangedEvent, value); }
+			remove { Events.RemoveHandler (SecurityChangedEvent, value); }
+		}
+
+		public event LoadStartedEventHandler LoadStarted
+		{
+			add { Events.AddHandler (LoadStartedEvent, value); }
+			remove { Events.RemoveHandler (LoadStartedEvent, value); }
+		}
+		
+		public event LoadCommitedEventHandler LoadCommited
+		{
+			add { Events.AddHandler (LoadCommitedEvent, value); }
+			remove { Events.RemoveHandler (LoadCommitedEvent, value); }
+		}		
+		
+		public event ProgressChangedEventHandler ProgressChanged
+		{
+			add { Events.AddHandler (ProgressChangedEvent, value); }
+			remove { Events.RemoveHandler (ProgressChangedEvent, value); }
+		}
+		
+		public event LoadFinishedEventHandler LoadFinished
+		{
+			add { Events.AddHandler (LoadFinishedEvent, value); }
+			remove { Events.RemoveHandler (LoadFinishedEvent, value); }
+		}
+
+		public event ContextMenuEventHandler ContextMenuShown
+		{
+			add { Events.AddHandler (ContextMenuEvent, value); }
+			remove { Events.RemoveHandler (ContextMenuEvent, value); }
+		}
+
+		public event NavigationRequestedEventHandler NavigationRequested
+		{
+			add { }
+			remove { }
+		}
+
+		internal static object GenericEvent = new object ();
+		internal event EventHandler Generic
+		{
+			add { Events.AddHandler (GenericEvent, value); }
+			remove { Events.RemoveHandler (GenericEvent, value); }
+		}
+
+		#endregion
+
+
+	}
+}
--- mono-4.2.1.102+dfsg2.orig/mcs/class/Mono.WebBrowser/Mono.WebBrowser.dll.sources
+++ mono-4.2.1.102+dfsg2/mcs/class/Mono.WebBrowser/Mono.WebBrowser.dll.sources
@@ -133,6 +133,9 @@ Mono.Mozilla/interfaces/nsIWebNavigation
 Mono.Mozilla/interfaces/extras/NodeType.cs
 Mono.Mozilla/interfaces/extras/nsIWriteSegmentFun.cs
 Mono.Mozilla/interfaces/extras/Options.cs
+Mono.NullBrowser/Base.cs
+Mono.NullBrowser/WebBrowser.cs
+Mono.NullBrowser/DOM/Navigation.cs
 Mono.WebBrowser/DOM/IDocumentType.cs
 Mono.WebBrowser/DOM/IDOMImplementation.cs
 Mono.WebBrowser/DOM/IMediaList.cs
--- mono-4.2.1.102+dfsg2.orig/mcs/class/Mono.WebBrowser/Mono.WebBrowser/Manager.cs
+++ mono-4.2.1.102+dfsg2/mcs/class/Mono.WebBrowser/Mono.WebBrowser/Manager.cs
@@ -50,8 +50,15 @@ namespace Mono.WebBrowser
 					browserEngine = null;
 				}
 			}
-			if (browserEngine == null || browserEngine == "mozilla")
-				return new Mono.Mozilla.WebBrowser (platform);
+			if (browserEngine == "mozilla") {
+				try {
+					return new Mono.Mozilla.WebBrowser (platform);
+				} catch {
+					browserEngine = null;
+				}
+			}
+			if (browserEngine == null)
+				return new Mono.NullBrowser.WebBrowser (platform);
 			throw new Exception (Mono.WebBrowser.Exception.ErrorCodes.EngineNotSupported, browserEngine);
 		}
 
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/ReferenceSources/DbConnectionStringDefaults.cs
@@ -0,0 +1,57 @@
+using System.Data.SqlClient;
+
+namespace System.Data.Common {
+    internal static class DbConnectionStringDefaults {
+        // all
+//        internal const string NamedConnection           = "";
+
+        // Odbc
+        internal const string Driver                    = "";
+        internal const string Dsn                       = "";
+
+        // OleDb
+        internal const bool   AdoNetPooler              = false;
+        internal const string FileName                  = "";
+        internal const int    OleDbServices             = ~(/*DBPROPVAL_OS_AGR_AFTERSESSION*/0x00000008 | /*DBPROPVAL_OS_CLIENTCURSOR*/0x00000004); // -13
+        internal const string Provider                  = "";
+
+        // OracleClient
+        internal const bool   Unicode                   = false;
+        internal const bool   OmitOracleConnectionName  = false;
+
+        // SqlClient
+        internal const ApplicationIntent ApplicationIntent = System.Data.SqlClient.ApplicationIntent.ReadWrite;
+        internal const string ApplicationName           = ".Net SqlClient Data Provider";
+        internal const bool   AsynchronousProcessing    = false;
+        internal const string AttachDBFilename          = "";
+        internal const int    ConnectTimeout            = 15;
+        internal const bool   ConnectionReset           = true;
+        internal const bool   ContextConnection         = false;
+        internal const string CurrentLanguage           = "";
+        internal const string DataSource                = "";
+        internal const bool   Encrypt                   = false;
+        internal const bool   Enlist                    = true;
+        internal const string FailoverPartner           = "";
+        internal const string InitialCatalog            = "";
+        internal const bool   IntegratedSecurity        = false;
+        internal const int    LoadBalanceTimeout        = 0; // default of 0 means don't use
+        internal const bool   MultipleActiveResultSets  = false;
+        internal const bool   MultiSubnetFailover       = false;
+        internal const int    MaxPoolSize               = 100;
+        internal const int    MinPoolSize               = 0;
+        internal const string NetworkLibrary            = "";
+        internal const int    PacketSize                = 8000;
+        internal const string Password                  = "";
+        internal const bool   PersistSecurityInfo       = false;
+        internal const bool   Pooling                   = true;
+        internal const bool   TrustServerCertificate    = false;
+        internal const string TypeSystemVersion         = "Latest";
+        internal const string UserID                    = "";
+        internal const bool   UserInstance              = false;
+        internal const bool   Replication               = false;
+        internal const string WorkstationID             = "";
+        internal const string TransactionBinding        = "Implicit Unbind";
+        internal const int    ConnectRetryCount         = 1;
+        internal const int    ConnectRetryInterval      = 10;
+    }
+}
\ No newline at end of file
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DataColumnMapping.cs
@@ -0,0 +1,131 @@
+//
+// System.Data.Common.DataColumnMapping.cs
+//
+// Authors:
+//   Rodrigo Moya (rodrigo@ximian.com)
+//   Tim Coleman (tim@timcoleman.com)
+//
+// (C) Ximian, Inc
+// Copyright (C) Tim Coleman, 2002-2003
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.ComponentModel;
+using System.Data;
+
+namespace System.Data.Common {
+#if NET_2_0
+	[TypeConverterAttribute ("System.Data.Common.DataColumnMapping+DataColumnMappingConverter, " + Consts.AssemblySystem_Data)]
+#else
+	[TypeConverterAttribute (typeof (DataColumnMappingConverter))]
+#endif
+	public sealed class DataColumnMapping : MarshalByRefObject, IColumnMapping, ICloneable
+	{
+		#region Fields
+
+		string sourceColumn;
+		string dataSetColumn;
+
+		#endregion // Fields
+
+		#region Constructors
+		
+		public DataColumnMapping () 
+		{
+			sourceColumn = String.Empty;
+			dataSetColumn = String.Empty;
+		}
+
+		public DataColumnMapping (string sourceColumn, string dataSetColumn) 
+		{
+			this.sourceColumn = sourceColumn;
+			this.dataSetColumn = dataSetColumn;
+		}
+
+		#endregion // Constructors
+
+		#region Properties
+
+#if ONLY_1_1
+		[DataSysDescription ("DataColumn.ColumnName")]
+#endif
+		[DefaultValue ("")]
+		public string DataSetColumn {
+			get { return dataSetColumn; }
+			set { dataSetColumn = value; }
+		}
+
+#if !NET_2_0
+		[DataSysDescription ("Source column name - case sensitive.")]
+#endif
+		[DefaultValue ("")]
+		public string SourceColumn {
+			get { return sourceColumn; }
+			set { sourceColumn = value; }
+		}
+
+		#endregion // Properties
+
+		#region Methods
+
+		[EditorBrowsable (EditorBrowsableState.Advanced)]
+		public DataColumn GetDataColumnBySchemaAction (DataTable dataTable, Type dataType, MissingSchemaAction schemaAction) 
+		{
+			if (dataTable.Columns.Contains (dataSetColumn))
+				return dataTable.Columns [dataSetColumn];
+			if (schemaAction == MissingSchemaAction.Ignore)
+				return null;
+			if (schemaAction == MissingSchemaAction.Error)
+				throw new InvalidOperationException (String.Format ("Missing the DataColumn '{0}' in the DataTable '{1}' for the SourceColumn '{2}'", DataSetColumn, dataTable.TableName, SourceColumn));
+			return new DataColumn (dataSetColumn, dataType);
+		}
+
+#if NET_2_0
+		[EditorBrowsable (EditorBrowsableState.Advanced)]
+		public static DataColumn GetDataColumnBySchemaAction (string sourceColumn, string dataSetColumn, DataTable dataTable, Type dataType, MissingSchemaAction schemaAction)
+		{
+			if (dataTable.Columns.Contains (dataSetColumn))
+				return dataTable.Columns [dataSetColumn];
+			if (schemaAction == MissingSchemaAction.Ignore)
+				return null;
+			if (schemaAction == MissingSchemaAction.Error)
+				throw new InvalidOperationException (String.Format ("Missing the DataColumn '{0}' in the DataTable '{1}' for the SourceColumn '{2}'", dataSetColumn, dataTable.TableName, sourceColumn));
+			return new DataColumn (dataSetColumn, dataType);
+		}
+#endif
+
+		object ICloneable.Clone ()
+		{
+			return new DataColumnMapping (SourceColumn, DataSetColumn);
+		}
+
+		public override string ToString ()
+		{
+			return SourceColumn; 
+		}
+
+		#endregion // Methods
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DataColumnMappingCollection.cs
@@ -0,0 +1,338 @@
+//
+// System.Data.Common.DataColumnMappingCollection
+//
+// Authors:
+//   Rodrigo Moya (rodrigo@ximian.com)
+//   Tim Coleman (tim@timcoleman.com)
+//
+// (C) Ximian, Inc
+// Copyright (C) Tim Coleman, 2002-2003
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Data;
+
+namespace System.Data.Common
+{
+	public sealed class DataColumnMappingCollection : MarshalByRefObject, IColumnMappingCollection , IList, ICollection, IEnumerable
+	{
+		#region Fields
+
+		readonly ArrayList list;
+		readonly Hashtable sourceColumns;
+		readonly Hashtable dataSetColumns;
+
+		#endregion // Fields
+
+		#region Constructors 
+
+		public DataColumnMappingCollection ()
+		{
+			list = new ArrayList ();
+			sourceColumns = new Hashtable ();
+			dataSetColumns = new Hashtable ();
+		}
+
+		#endregion // Constructors
+
+		#region Properties
+
+		[Browsable (false)]
+#if !NET_2_0
+		[DataSysDescription ("The number of items in the collection")]
+#endif
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		public int Count {
+			get { return list.Count; }
+		}
+
+		[Browsable (false)]
+#if !NET_2_0
+		[DataSysDescription ("The specified DataColumnMapping object.")]
+#endif
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		public DataColumnMapping this [int index] {
+			get { return (DataColumnMapping)(list[index]); }
+			set { 
+				DataColumnMapping mapping = (DataColumnMapping)(list[index]);
+				sourceColumns[mapping] = value;
+				dataSetColumns[mapping] = value;
+				list[index] = value;
+			}
+		}
+
+		[Browsable (false)]
+#if !NET_2_0
+		[DataSysDescription ("The specified DataColumnMapping object.")]
+#endif
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		public DataColumnMapping this [string sourceColumn] {
+			get {
+				if (!Contains(sourceColumn))
+					throw new IndexOutOfRangeException("DataColumnMappingCollection doesn't contain DataColumnMapping with SourceColumn '" + sourceColumn + "'.");
+				return (DataColumnMapping) sourceColumns [sourceColumn];
+			}
+			set {
+				this [list.IndexOf (sourceColumns [sourceColumn])] = value;
+			}
+		}
+
+		object ICollection.SyncRoot {
+			get { return list.SyncRoot; }
+		}
+
+		bool ICollection.IsSynchronized {
+			get { return list.IsSynchronized; }
+		}
+
+		object IColumnMappingCollection.this [string index] {
+			get { return this [index]; }
+			set {
+				if (!(value is DataColumnMapping))
+					throw new ArgumentException ();
+				this [index] = (DataColumnMapping) value;
+			}
+		}
+
+		object IList.this [int index] {
+			get { return this [index]; }
+			set {
+				if (!(value is DataColumnMapping))
+					throw new ArgumentException ();
+				this [index] = (DataColumnMapping) value;
+			}
+		}
+
+		bool IList.IsReadOnly {
+			get { return false; }
+		}
+
+		bool IList.IsFixedSize {
+			get { return false; }
+		}
+		
+		#endregion // Properties
+
+		#region Methods
+
+		public int Add (object value)
+		{
+			if (!(value is DataColumnMapping))
+				throw new InvalidCastException ();
+
+			list.Add (value);
+			sourceColumns [((DataColumnMapping) value).SourceColumn] = value;
+			dataSetColumns [((DataColumnMapping )value).DataSetColumn] = value;
+			return list.IndexOf (value);
+		}
+
+		public DataColumnMapping Add (string sourceColumn, string dataSetColumn)
+		{
+			DataColumnMapping mapping = new DataColumnMapping (sourceColumn, dataSetColumn);
+			Add (mapping);
+			return mapping;
+		}
+
+#if NET_2_0
+		public void AddRange (Array values)
+		{
+			for (int i = 0; i < values.Length; ++i)
+				Add (values.GetValue (i));
+		}
+#endif
+
+		public void AddRange (DataColumnMapping[] values)
+		{
+			foreach (DataColumnMapping mapping in values)
+				Add (mapping);
+		}
+
+		public void Clear ()
+		{
+			list.Clear ();
+		}
+
+		public bool Contains (object value)
+		{
+			if (!(value is DataColumnMapping))
+				throw new InvalidCastException("Object is not of type DataColumnMapping");
+			return (list.Contains (value));
+		}
+
+		public bool Contains (string value)
+		{
+			return (sourceColumns.Contains (value));
+		}
+
+		public void CopyTo (Array array, int index)
+		{
+			list.CopyTo (array,index);
+		}
+
+#if NET_2_0
+		public void CopyTo (DataColumnMapping [] array, int index)
+		{
+			list.CopyTo (array, index);
+		}
+#endif
+
+		public DataColumnMapping GetByDataSetColumn (string value)
+		{
+			// this should work case-insenstive.
+			if (!(dataSetColumns [value] == null))
+				return (DataColumnMapping) (dataSetColumns [value]);
+			else {
+				string lowcasevalue = value.ToLower ();
+				object [] keyarray = new object [dataSetColumns.Count];
+				dataSetColumns.Keys.CopyTo (keyarray, 0);
+				for (int i = 0; i < keyarray.Length; i++) {
+					string temp = (string) keyarray [i];
+					if (lowcasevalue.Equals (temp.ToLower ()))
+						return (DataColumnMapping) (dataSetColumns [keyarray [i]]);
+				}
+				return null;
+			}
+		}
+
+		[EditorBrowsable (EditorBrowsableState.Advanced)]
+		public static DataColumnMapping GetColumnMappingBySchemaAction (DataColumnMappingCollection columnMappings, string sourceColumn, MissingMappingAction mappingAction)
+		{
+			if (columnMappings.Contains (sourceColumn))
+				return columnMappings[sourceColumn];
+			if (mappingAction == MissingMappingAction.Ignore)
+				return null;
+			if (mappingAction == MissingMappingAction.Error)
+				throw new InvalidOperationException (String.Format ("Missing SourceColumn mapping for '{0}'", sourceColumn));
+			return new DataColumnMapping (sourceColumn, sourceColumn);
+		}
+
+#if NET_2_0
+		[MonoTODO]
+		[EditorBrowsable (EditorBrowsableState.Advanced)]
+		public static DataColumn GetDataColumn (DataColumnMappingCollection columnMappings, string sourceColumn, Type dataType, DataTable dataTable, MissingMappingAction mappingAction, MissingSchemaAction schemaAction)
+		{
+			throw new NotImplementedException ();
+		}
+#endif
+
+		public IEnumerator GetEnumerator ()
+		{
+			return list.GetEnumerator ();
+		}
+
+		IColumnMapping IColumnMappingCollection.Add (string sourceColumnName, string dataSetColumnName)
+		{
+			return Add (sourceColumnName, dataSetColumnName);
+		}
+
+		IColumnMapping IColumnMappingCollection.GetByDataSetColumn (string dataSetColumnName)
+		{
+			return GetByDataSetColumn (dataSetColumnName);
+		}
+
+		public int IndexOf (object value)
+		{
+			return list.IndexOf (value);
+		}
+
+		public int IndexOf (string sourceColumn)
+		{
+			return list.IndexOf (sourceColumns [sourceColumn]);
+		}
+
+		public int IndexOfDataSetColumn (string dataSetColumn)
+		{
+			// this should work case-insensitive
+			if (!(dataSetColumns [dataSetColumn] == null))
+				return list.IndexOf (dataSetColumns [dataSetColumn]);
+			else {
+				string lowcasevalue = dataSetColumn.ToLower ();
+				object [] keyarray = new object[dataSetColumns.Count];
+				dataSetColumns.Keys.CopyTo (keyarray,0);
+				for (int i = 0; i < keyarray.Length; i++) {
+					string temp = (string) keyarray [i];
+					if (lowcasevalue.Equals (temp.ToLower ()))
+						return list.IndexOf (dataSetColumns [keyarray [i]]);
+				}
+				return -1;
+			}
+		}
+
+		public void Insert (int index, object value)
+		{
+			list.Insert (index, value);
+			sourceColumns [((DataColumnMapping) value).SourceColumn] = value;
+			dataSetColumns [((DataColumnMapping) value).DataSetColumn] = value;
+		}
+
+#if NET_2_0
+		public void Insert (int index, DataColumnMapping value)
+		{
+			list.Insert (index, value);
+			sourceColumns [value.SourceColumn] = value;
+			dataSetColumns [value.DataSetColumn] = value;
+		}
+#endif
+
+		public void Remove (object value)
+		{
+			int index = list.IndexOf (value);
+			sourceColumns.Remove (((DataColumnMapping) value).SourceColumn);
+			dataSetColumns.Remove (((DataColumnMapping) value).DataSetColumn);
+			if (index < 0 || index >=list.Count)
+				throw new ArgumentException("There is no such element in collection.");
+			list.Remove (value);
+		}
+
+#if NET_2_0
+		public void Remove (DataColumnMapping value)
+		{
+			int index = list.IndexOf (value);
+			sourceColumns.Remove (value.SourceColumn);
+			dataSetColumns.Remove (value.DataSetColumn);
+			if ( index < 0 || index >=list.Count)
+				throw new ArgumentException("There is no such element in collection.");
+			list.Remove (value);
+		}
+#endif
+
+		public void RemoveAt (int index)
+		{
+			if (index < 0 || index >=list.Count)
+				throw new IndexOutOfRangeException("There is no element in collection.");
+			Remove (list [index]);
+		}
+
+		public void RemoveAt (string sourceColumn)
+		{
+			RemoveAt (list.IndexOf (sourceColumns [sourceColumn]));
+		}
+
+		#endregion // Methods
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DataColumnMappingConverter.cs
@@ -0,0 +1,59 @@
+//
+// System.Data.Common.DataColumnMappingConverter.cs
+//
+// Author:
+//   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
+//
+// (C) 2004 Andreas Nahr
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Globalization;
+using System.ComponentModel;
+
+namespace System.Data.Common
+{
+	internal sealed class DataColumnMappingConverter : ExpandableObjectConverter
+	{
+		[MonoTODO]
+		public DataColumnMappingConverter ()
+		{
+			throw new NotImplementedException ();
+		}
+
+		[MonoTODO]
+		public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+		{
+			throw new NotImplementedException ();
+		}
+
+		[MonoTODO]
+		public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
+		{
+			throw new NotImplementedException ();
+		}
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DataTableMapping.cs
@@ -0,0 +1,159 @@
+//
+// System.Data.Common.DataTableMapping.cs
+//
+// Authors:
+//   Rodrigo Moya (rodrigo@ximian.com)
+//   Tim Coleman (tim@timcoleman.com)
+//
+// (C) Ximian, Inc
+// Copyright (C) Tim Coleman, 2002-2003
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.ComponentModel;
+using System.Data;
+
+namespace System.Data.Common {
+#if NET_2_0
+	[TypeConverterAttribute ("System.Data.Common.DataTableMapping+DataTableMappingConverter, " + Consts.AssemblySystem_Data)]
+#else
+	[TypeConverterAttribute (typeof (DataTableMappingConverter))]
+#endif
+	public sealed class DataTableMapping : MarshalByRefObject, ITableMapping, ICloneable
+	{
+		#region Fields
+
+		string sourceTable;
+		string dataSetTable;
+		DataColumnMappingCollection columnMappings;
+
+		#endregion // Fields
+
+		#region Constructors
+
+		public DataTableMapping () 
+		{
+			dataSetTable = String.Empty;
+			sourceTable = String.Empty;
+			columnMappings = new DataColumnMappingCollection ();
+		}
+
+		public DataTableMapping (string sourceTable, string dataSetTable) 
+			: this ()
+		{
+			this.sourceTable = sourceTable;
+			this.dataSetTable = dataSetTable;
+		}
+		
+		public DataTableMapping (string sourceTable, string dataSetTable, DataColumnMapping[] columnMappings) 
+			: this (sourceTable, dataSetTable)
+		{
+			this.columnMappings.AddRange (columnMappings);
+		}
+
+		#endregion // Constructors
+
+		#region Properties
+
+#if !NET_2_0
+		[DataSysDescription ("Individual columns mappings when this table mapping is matched.")]
+#endif
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
+		public DataColumnMappingCollection ColumnMappings {
+			get { return columnMappings; }
+		}
+
+#if !NET_2_0
+		[DataSysDescription ("DataTable.TableName")]
+#endif
+		[DefaultValue ("")]
+		public string DataSetTable {
+			get { return dataSetTable; } 
+			set { dataSetTable = value; }
+		}
+
+		IColumnMappingCollection ITableMapping.ColumnMappings {
+			get { return ColumnMappings; }
+		}
+	
+#if !NET_2_0
+		[DataSysDescription ("The DataTableMapping source table name. This name is case sensitive.")]
+#endif
+		[DefaultValue ("")]
+		public string SourceTable {
+			get { return sourceTable; }
+			set { sourceTable = value; }
+		}
+
+		#endregion // Properties
+
+		#region Methods
+
+		[EditorBrowsable (EditorBrowsableState.Advanced)]
+		public DataColumnMapping GetColumnMappingBySchemaAction (string sourceColumn, MissingMappingAction mappingAction) 
+		{
+			return DataColumnMappingCollection.GetColumnMappingBySchemaAction (columnMappings, sourceColumn, mappingAction);
+		}
+
+#if NET_2_0
+		[MonoTODO]
+		[EditorBrowsable (EditorBrowsableState.Advanced)]
+		public DataColumn GetDataColumn (string sourceColumn, 
+						 Type dataType, 
+						 DataTable dataTable, 
+						 MissingMappingAction mappingAction, 
+						 MissingSchemaAction schemaAction)
+		{
+			throw new NotImplementedException ();
+		}
+#endif
+
+		[EditorBrowsable (EditorBrowsableState.Advanced)]
+		public DataTable GetDataTableBySchemaAction (DataSet dataSet, MissingSchemaAction schemaAction) 
+		{
+			if (dataSet.Tables.Contains (DataSetTable))
+				return dataSet.Tables [DataSetTable];
+			if (schemaAction == MissingSchemaAction.Ignore)
+				return null;
+			if (schemaAction == MissingSchemaAction.Error)
+				throw new InvalidOperationException (String.Format ("Missing the '{0} DataTable for the '{1}' SourceTable", DataSetTable, SourceTable));
+			return new DataTable (DataSetTable);
+		}
+
+		object ICloneable.Clone ()
+		{
+			DataColumnMapping [] arr = new DataColumnMapping [columnMappings.Count];
+			columnMappings.CopyTo (arr, 0);
+			return new DataTableMapping (SourceTable, DataSetTable, arr);
+		}
+
+		public override string ToString ()
+		{
+			return SourceTable; 
+		}
+		
+		#endregion // Methods
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DataTableMappingCollection.cs
@@ -0,0 +1,326 @@
+//
+// System.Data.Common.DataTableMappingCollection.cs
+//
+// Author:
+//   Rodrigo Moya (rodrigo@ximian.com)
+//   Tim Coleman (tim@timcoleman.com)
+//
+// (C) Ximian, Inc
+// Copyright (C) Tim Coleman, 2002-2003
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+
+namespace System.Data.Common
+{
+	[ListBindable (false)]
+	[EditorAttribute ("Microsoft.VSDesigner.Data.Design.DataTableMappingCollectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
+	public sealed class DataTableMappingCollection : MarshalByRefObject, ITableMappingCollection, IList, ICollection, IEnumerable
+	{
+		#region Fields
+
+		ArrayList mappings;
+		Hashtable sourceTables;
+		Hashtable dataSetTables;
+
+		#endregion
+
+		#region Constructors 
+
+		public DataTableMappingCollection() 
+		{
+			mappings = new ArrayList ();
+			sourceTables = new Hashtable ();
+			dataSetTables = new Hashtable ();
+		}
+
+		#endregion // Constructors
+
+		#region Properties
+
+		[Browsable (false)]
+#if !NET_2_0
+		[DataSysDescription ("The number of items in the collection")]
+#endif
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		public int Count {
+			get { return mappings.Count; }
+		}
+
+		[Browsable (false)]
+#if !NET_2_0
+		[DataSysDescription ("The specified DataTableMapping object")]
+#endif
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		public DataTableMapping this [int index] {
+			get { return (DataTableMapping)(mappings[index]); }
+			set {
+				DataTableMapping mapping = (DataTableMapping) mappings[index];
+				sourceTables [mapping.SourceTable] = value;
+				dataSetTables [mapping.DataSetTable] = value;
+				mappings [index] = value; 
+			}
+		}
+
+		[Browsable (false)]
+#if !NET_2_0
+		[DataSysDescription ("The specified DataTableMapping object")]
+#endif
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		public DataTableMapping this [string sourceTable] {
+			get { return (DataTableMapping) sourceTables[sourceTable]; }
+			set { this [mappings.IndexOf (sourceTables[sourceTable])] = value; }
+		}
+
+		object IList.this [int index] {
+			get { return (object)(this[index]); }
+			set {
+				if (!(value is DataTableMapping))
+					throw new ArgumentException (); 
+				this[index] = (DataTableMapping)value;
+			}
+		}
+
+		bool ICollection.IsSynchronized {
+			get { return mappings.IsSynchronized; }
+		}
+
+		object ICollection.SyncRoot {
+			get { return mappings.SyncRoot; }
+		}
+
+		bool IList.IsFixedSize {
+			get { return false; }
+		}
+
+		bool IList.IsReadOnly {
+			get { return false; }
+		}
+
+		object ITableMappingCollection.this [string index] {
+			get { return this [index]; }
+			set {
+				if (!(value is DataTableMapping))
+					throw new ArgumentException ();
+				this [index] = (DataTableMapping) value;
+			}
+		}
+
+		#endregion // Properties
+
+		#region Methods
+
+		public int Add (object value)
+		{
+			if (!(value is System.Data.Common.DataTableMapping))
+				throw new InvalidCastException ("The object passed in was not a DataTableMapping object.");
+
+			sourceTables [((DataTableMapping) value).SourceTable] = value;
+			dataSetTables [((DataTableMapping) value).DataSetTable] = value;
+			return mappings.Add (value);
+		}
+
+		public DataTableMapping Add (string sourceTable, string dataSetTable) 
+		{
+			DataTableMapping mapping = new DataTableMapping (sourceTable, dataSetTable);
+			Add (mapping);
+			return mapping;
+		}
+
+#if NET_2_0
+		public void AddRange (Array values)
+		{
+			for (int i = 0; i < values.Length; ++i)
+				Add (values.GetValue (i));
+		}
+#endif
+
+		public void AddRange (DataTableMapping[] values)
+		{
+			foreach (DataTableMapping dataTableMapping in values)
+				this.Add (dataTableMapping);
+		}
+
+		public void Clear ()
+		{
+			sourceTables.Clear ();
+			dataSetTables.Clear ();
+			mappings.Clear ();
+		}
+
+		public bool Contains (object value)
+		{
+			return mappings.Contains (value);
+		}
+
+		public bool Contains (string value)
+		{
+			return sourceTables.Contains (value);
+		}
+
+		public void CopyTo (Array array, int index)
+		{
+			mappings.CopyTo (array, index);
+		}
+
+#if NET_2_0
+		public void CopyTo (DataTableMapping[] array, int index) 
+		{
+			mappings.CopyTo (array, index);
+		}
+#endif
+
+		public DataTableMapping GetByDataSetTable (string dataSetTable) 
+		{
+			// this should work case-insenstive.
+			if (!(dataSetTables[dataSetTable] == null))
+				return (DataTableMapping) (dataSetTables [dataSetTable]);
+			else {
+				string lowcasevalue = dataSetTable.ToLower ();
+				object [] keyarray = new object [dataSetTables.Count];
+				dataSetTables.Keys.CopyTo (keyarray, 0);
+				for (int i=0; i<keyarray.Length; i++) {
+					string temp = (string) keyarray [i];
+					if (lowcasevalue.Equals (temp.ToLower ()))
+						return (DataTableMapping) (dataSetTables [keyarray [i]]);
+				}
+				return null;
+			}
+		}
+
+		[EditorBrowsable (EditorBrowsableState.Advanced)]
+		public static DataTableMapping GetTableMappingBySchemaAction (DataTableMappingCollection tableMappings, string sourceTable, string dataSetTable, MissingMappingAction mappingAction) 
+		{
+			if (tableMappings.Contains (sourceTable))
+				return tableMappings[sourceTable];
+			if (mappingAction == MissingMappingAction.Error)
+				throw new InvalidOperationException (String.Format ("Missing source table mapping: '{0}'",
+										    sourceTable));
+			if (mappingAction == MissingMappingAction.Ignore)
+				return null;
+			return new DataTableMapping (sourceTable, dataSetTable);
+		}
+
+		public IEnumerator GetEnumerator ()
+		{
+			return mappings.GetEnumerator ();
+		}
+
+		public int IndexOf (object value) 
+		{
+			return mappings.IndexOf (value);
+		}
+
+		public int IndexOf (string sourceTable) 
+		{
+			return IndexOf (sourceTables[sourceTable]);
+		}
+
+		public int IndexOfDataSetTable (string dataSetTable) 
+		{
+			// this should work case-insensitive
+			if (!(dataSetTables[dataSetTable] == null)) 
+				return IndexOf ((DataTableMapping)(dataSetTables[dataSetTable]));
+			else {
+				string lowcasevalue = dataSetTable.ToLower();
+				object [] keyarray = new object[dataSetTables.Count];
+				dataSetTables.Keys.CopyTo(keyarray,0);
+				for (int i=0; i<keyarray.Length; i++) {
+					string temp = (string) keyarray[i];
+					if (lowcasevalue.Equals(temp.ToLower()))
+						return IndexOf ((DataTableMapping)(dataSetTables[keyarray[i]]));
+				}
+				return -1;
+			}
+
+		}
+
+		public void Insert (int index, object value) 
+		{
+			mappings.Insert (index, value);
+			sourceTables [((DataTableMapping) value).SourceTable] = value;
+			dataSetTables [((DataTableMapping) value).DataSetTable] = value;
+		}
+
+#if NET_2_0
+		public void Insert (int index, DataTableMapping value) 
+		{
+			mappings.Insert (index, value);
+			sourceTables [value.SourceTable] = value;
+			dataSetTables [value.DataSetTable] = value;
+		}
+#endif
+
+		ITableMapping ITableMappingCollection.Add (string sourceTableName, string dataSetTableName)
+		{
+			ITableMapping tableMapping = new DataTableMapping (sourceTableName, dataSetTableName);
+			Add (tableMapping);
+			return tableMapping;
+		}
+
+		ITableMapping ITableMappingCollection.GetByDataSetTable (string dataSetTableName)
+		{
+			return this [mappings.IndexOf (dataSetTables [dataSetTableName])];
+		}
+
+		public void Remove (object value) 
+		{
+			if (!(value is DataTableMapping))
+				throw new InvalidCastException ();
+			int index = mappings.IndexOf (value);
+			if (index < 0 || index >= mappings.Count)
+				throw new ArgumentException("There is no such element in collection.");
+			mappings.Remove ((DataTableMapping) value);
+		}
+
+#if NET_2_0
+		public void Remove (DataTableMapping value) 
+		{
+			int index = mappings.IndexOf (value);
+			if (index < 0 || index >= mappings.Count)
+				throw new ArgumentException("There is no such element in collection."); 
+			mappings.Remove ((DataTableMapping) value);
+		}
+#endif
+
+		public void RemoveAt (int index) 
+		{
+			 if (index < 0 || index >= mappings.Count)
+				throw new IndexOutOfRangeException("There is no element in collection.");
+
+			mappings.RemoveAt (index);
+		}
+
+		public void RemoveAt (string sourceTable) 
+		{
+			RemoveAt (mappings.IndexOf (sourceTables[sourceTable]));
+		}
+
+		#endregion // Methods
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DataTableMappingConverter.cs
@@ -0,0 +1,59 @@
+//
+// System.Data.Common.DataTableMappingConverter.cs
+//
+// Author:
+//   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
+//
+// (C) 2004 Andreas Nahr
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Globalization;
+using System.ComponentModel;
+
+namespace System.Data.Common
+{
+	internal sealed class DataTableMappingConverter : ExpandableObjectConverter
+	{
+		[MonoTODO]
+		public DataTableMappingConverter ()
+		{
+			throw new NotImplementedException ();
+		}
+
+		[MonoTODO]
+		public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+		{
+			throw new NotImplementedException ();
+		}
+
+		[MonoTODO]
+		public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
+		{
+			throw new NotImplementedException ();
+		}
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DbConnectionOptions.cs
@@ -0,0 +1,257 @@
+//
+// System.Data.Common.DbConnectionOptions
+//	adapted from older (pre beta1) DbConnectionString
+//
+// Authors:
+//	Tim Coleman (tim@timcoleman.com)
+//	Sebastien Pouliot  <sebastien@ximian.com>
+//
+// Copyright (C) Tim Coleman, 2003
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if NET_2_0
+
+using System.Collections;
+using System.Collections.Specialized;
+using System.Security;
+using System.Text;
+
+namespace System.Data.Common {
+
+	internal class DbConnectionOptions {
+
+		#region Fields
+
+		internal NameValueCollection options;
+		internal string normalizedConnectionString;
+
+		#endregion // Fields
+
+		#region Constructors
+
+		internal DbConnectionOptions ()
+		{
+		}
+
+		protected internal DbConnectionOptions (DbConnectionOptions connectionOptions)
+		{
+			options = connectionOptions.options;
+		}
+
+		public DbConnectionOptions (string connectionString)
+		{
+			options = new NameValueCollection ();
+			ParseConnectionString (connectionString);
+		}
+		
+		[MonoTODO]
+		public DbConnectionOptions (string connectionString, Hashtable synonyms, bool useFirstKeyValuePair)
+			: this (connectionString)
+		{
+		}
+
+		#endregion // Constructors
+
+		#region Properties
+
+		[MonoTODO]
+		public bool IsEmpty {
+			get { throw new NotImplementedException (); }
+		}
+
+		public string this [string keyword] {
+			get { return options [keyword]; }
+		}
+
+		public ICollection Keys {
+			get { return options.Keys; }
+		}
+
+		#endregion // Properties
+
+		#region Methods
+
+		[MonoTODO]
+		protected void BuildConnectionString (StringBuilder builder, string[] withoutOptions, string insertValue)
+		{
+			throw new NotImplementedException ();
+		}
+
+		public bool ContainsKey (string keyword)
+		{
+			return (options.Get (keyword) != null);
+		}
+
+		public bool ConvertValueToBoolean (string keyname, bool defaultvalue)
+		{
+			if (ContainsKey (keyname))
+				return Boolean.Parse (this [keyname].Trim ());
+			return defaultvalue;
+		}
+
+		public int ConvertValueToInt32 (string keyname, int defaultvalue)
+		{
+			if (ContainsKey (keyname))
+				return Int32.Parse (this [keyname].Trim ());
+			return defaultvalue;
+		}
+
+		[MonoTODO]
+		public bool ConvertValueToIntegratedSecurity ()
+		{
+			throw new NotImplementedException ();
+		}
+
+		public string ConvertValueToString (string keyname, string defaultValue)
+		{
+			if (ContainsKey (keyname))
+				return this [keyname];
+			return defaultValue;
+		}
+
+		[MonoTODO]
+		protected internal virtual PermissionSet CreatePermissionSet ()
+		{
+			throw new NotImplementedException ();
+		}
+
+		[MonoTODO]
+		protected internal virtual string Expand ()
+		{
+			throw new NotImplementedException ();
+		}
+
+		[MonoTODO]
+		public static string RemoveKeyValuePairs (string connectionString, string[] keynames)
+		{
+			throw new NotImplementedException ();
+		}
+
+		[MonoTODO]
+		public string UsersConnectionString (bool hisPasswordPwd)
+		{
+			throw new NotImplementedException ();
+		}
+
+		internal void ParseConnectionString (string connectionString)
+		{
+			if (connectionString.Length == 0)
+				return;
+
+			connectionString += ";";
+
+			bool inQuote = false;
+			bool inDQuote = false;
+			bool inName = true;
+
+			string name = String.Empty;
+			string value = String.Empty;
+			StringBuilder sb = new StringBuilder ();
+
+			for (int i = 0; i < connectionString.Length; i += 1) {
+				char c = connectionString [i];
+				char peek;
+				if (i == connectionString.Length - 1)
+					peek = '\0';
+				else 
+					peek = connectionString [i + 1];
+
+				switch (c) {
+				case '\'':
+					if (inDQuote) 
+						sb.Append (c);
+					else if (peek.Equals (c)) {
+						sb.Append (c);
+						i += 1;
+					}
+					else 
+						inQuote = !inQuote;
+					break;
+				case '"':
+					if (inQuote) 
+						sb.Append (c);
+					else if (peek.Equals (c)) {
+						sb.Append (c);
+						i += 1;
+					}
+					else 
+						inDQuote = !inDQuote;
+					break;
+				case ';':
+					if (inDQuote || inQuote)
+						sb.Append (c);
+					else {
+						if (name != String.Empty && name != null) {
+							value = sb.ToString ();
+							// FIXME - KeywordLookup is an NOP
+							// options [KeywordLookup (name.Trim ())] = value;
+							options [name.Trim ()] = value;
+						}
+						inName = true;
+						name = String.Empty;
+						value = String.Empty;
+						sb = new StringBuilder ();
+					}
+					break;
+				case '=':
+					if (inDQuote || inQuote || !inName)
+						sb.Append (c);
+					else if (peek.Equals (c)) {
+						sb.Append (c);
+						i += 1;
+					} 
+					else {
+						name = sb.ToString ();
+						sb = new StringBuilder ();
+						inName = false;
+					}
+					break;
+				case ' ':
+					if (inQuote || inDQuote)
+						sb.Append (c);
+					else if (sb.Length > 0 && !peek.Equals (';'))
+						sb.Append (c);
+					break;
+				default:
+					sb.Append (c);
+					break;
+				}
+			}	
+			
+			StringBuilder normalized = new StringBuilder ();
+			ArrayList keys = new ArrayList ();
+			keys.AddRange (Keys);
+			keys.Sort ();
+			foreach (string key in keys)
+			{
+				string entry = String.Format ("{0}=\"{1}\";", key, this [key].Replace ("\"", "\"\""));
+				normalized.Append (entry);
+			}
+			normalizedConnectionString = normalized.ToString ();
+		}
+
+		#endregion // Methods
+	}
+}
+
+#endif
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DbConnectionString.cs
@@ -0,0 +1,113 @@
+//
+// System.Data.Common.DbConnectionString
+//
+// Authors:
+//	Tim Coleman (tim@timcoleman.com)
+//	Sebastien Pouliot  <sebastien@ximian.com>
+//
+// Copyright (C) Tim Coleman, 2003
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if NET_2_0
+
+using System.Collections;
+using System.Collections.Specialized;
+using System.Runtime.Serialization;
+using System.Text;
+
+namespace System.Data.Common {
+
+	[Obsolete ()]
+	internal class DbConnectionString : DbConnectionOptions, ISerializable {
+
+		#region Fields
+
+		KeyRestrictionBehavior behavior;
+
+		#endregion // Fields
+
+		#region Constructors
+
+		protected internal DbConnectionString (DbConnectionString constr)
+		{
+			options = constr.options;
+		}
+
+		public DbConnectionString (string connectionString)
+			: base (connectionString)
+		{
+			options = new NameValueCollection ();
+			ParseConnectionString (connectionString);
+		}
+		
+		[MonoTODO]
+		protected DbConnectionString (SerializationInfo si, StreamingContext sc)
+		{
+		}
+
+		[MonoTODO]
+		public DbConnectionString (string connectionString, string restrictions, KeyRestrictionBehavior behavior)
+			: this (connectionString)
+		{
+			this.behavior = behavior;
+		}
+
+		#endregion // Constructors
+
+		#region Properties
+
+		public KeyRestrictionBehavior Behavior {
+			get { return behavior; }
+		}
+
+		[MonoTODO]
+		public string Restrictions {
+			get { throw new NotImplementedException (); }
+		}
+		
+		#endregion // Properties
+
+		#region Methods
+
+		[MonoTODO]
+		public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
+		{
+			throw new NotImplementedException ();
+		}
+
+		protected virtual string KeywordLookup (string keyname)
+		{
+			return keyname;
+		}
+
+		[MonoTODO]
+		public virtual void PermissionDemand ()
+		{
+			throw new NotImplementedException ();
+		}
+
+		#endregion // Methods
+	}
+}
+
+#endif
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DbConnectionStringBuilder.cs
@@ -0,0 +1,802 @@
+//
+// System.Data.Common.DbConnectionStringBuilder.cs
+//
+// Author:
+//	Sureshkumar T (tsureshkumar@novell.com)
+//	Gert Driesen (drieseng@users.sourceforge.net
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if NET_2_0
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Data;
+using System.Data.Common;
+using System.Reflection;
+using System.Text;
+
+namespace System.Data.Common
+{
+	public class DbConnectionStringBuilder : IDictionary, ICollection, IEnumerable, ICustomTypeDescriptor
+	{
+		#region Fields
+
+		readonly Dictionary<string, object> _dictionary;
+		readonly bool useOdbcRules;
+
+		#endregion Fields
+
+		#region Constructors
+
+		public DbConnectionStringBuilder () : this (false)
+		{
+		}
+
+		public DbConnectionStringBuilder (bool useOdbcRules)
+		{
+			this.useOdbcRules = useOdbcRules;
+			_dictionary = new Dictionary <string, object> (StringComparer.InvariantCultureIgnoreCase);
+		}
+
+		#endregion // Constructors
+
+		#region Properties
+
+		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+		[EditorBrowsable (EditorBrowsableState.Never)]
+		[Browsable (false)]
+		[DesignOnly (true)]
+		public bool BrowsableConnectionString {
+			get { throw new NotImplementedException (); }
+			set { throw new NotImplementedException (); }
+		}
+
+		[RefreshProperties (RefreshProperties.All)]
+		public string ConnectionString {
+			get {
+				IDictionary<string, object> dictionary = (IDictionary <string, object>) _dictionary;
+				StringBuilder sb = new StringBuilder ();
+				foreach (string key in Keys) {
+					object value = null;
+					if (!dictionary.TryGetValue (key, out value))
+						continue;
+					string val = value.ToString ();
+					AppendKeyValuePair (sb, key, val, useOdbcRules);
+				}
+				return sb.ToString ();
+			}
+			set {
+				Clear ();
+				if (value == null)
+					return;
+				if (value.Trim ().Length == 0)
+					return;
+				ParseConnectionString (value);
+			}
+		}
+
+		[Browsable (false)]
+                public virtual int Count
+                {
+                        get { return _dictionary.Count; }
+                }
+
+		[Browsable (false)]
+                public virtual bool IsFixedSize
+                {
+                        get { return false; }
+                }
+
+		[Browsable (false)]
+                public bool IsReadOnly
+                {
+                        get { throw new NotImplementedException (); }
+                }
+
+		[Browsable (false)]
+		public virtual object this [string keyword] {
+			get {
+				if (ContainsKey (keyword))
+					return _dictionary [keyword];
+				else
+					throw new ArgumentException (string.Format (
+						"Keyword '{0}' does not exist",
+						keyword));
+			}
+			set {
+				if (value == null) {
+					Remove (keyword);
+					return;
+				}
+
+				if (keyword == null)
+					throw new ArgumentNullException ("keyword");
+
+				if (keyword.Length == 0)
+					throw CreateInvalidKeywordException (keyword);
+
+				for (int i = 0; i < keyword.Length; i++) {
+					char c = keyword [i];
+					if (i == 0 && (Char.IsWhiteSpace (c) || c == ';'))
+						throw CreateInvalidKeywordException (keyword);
+					if (i == (keyword.Length - 1) && Char.IsWhiteSpace (c))
+						throw CreateInvalidKeywordException (keyword);
+					if (Char.IsControl (c))
+						throw CreateInvalidKeywordException (keyword);
+				}
+
+				if (ContainsKey (keyword))
+					_dictionary [keyword] = value;
+				else
+					_dictionary.Add (keyword, value);
+			}
+		}
+
+		[Browsable (false)]
+		public virtual ICollection Keys
+		{
+			get {
+				string [] keys = new string [_dictionary.Keys.Count];
+				((ICollection<string>) _dictionary.Keys).CopyTo (keys, 0);
+				ReadOnlyCollection<string> keyColl = new ReadOnlyCollection<string> (keys);
+				return keyColl; 
+			}
+		}
+
+                bool ICollection.IsSynchronized
+                {
+                        get { throw new NotImplementedException (); }
+                }
+
+                object ICollection.SyncRoot
+                {
+                        get { throw new NotImplementedException (); }
+                }
+
+                object IDictionary.this [object keyword]
+                {
+                        get { return this [(string) keyword]; }
+                        set { this [(string) keyword] = value; }
+                }
+
+		[Browsable (false)]
+		public virtual ICollection Values {
+			get {
+				object [] values = new object [_dictionary.Values.Count];
+				((ICollection<object>) _dictionary.Values).CopyTo (values, 0);
+				ReadOnlyCollection<object> valuesColl = new ReadOnlyCollection<object> (values);
+				return valuesColl; 
+			}
+		}
+
+		#endregion // Properties
+
+		#region Methods
+
+		public void Add (string keyword, object value)
+		{
+			this [keyword] = value;
+		}
+
+		public static void AppendKeyValuePair (StringBuilder builder, string keyword, string value,
+						       bool useOdbcRules)
+		{
+			if (builder == null)
+				throw new ArgumentNullException ("builder");
+			if (keyword == null)
+				throw new ArgumentNullException ("keyName");
+			if (keyword.Length == 0)
+				throw new ArgumentException ("Empty keyword is not valid.");
+
+			if (builder.Length > 0)
+				builder.Append (';');
+			if (!useOdbcRules)
+				builder.Append (keyword.Replace ("=", "=="));
+			else
+				builder.Append (keyword);
+			builder.Append ('=');
+
+			if (value == null || value.Length == 0)
+				return;
+
+			if (!useOdbcRules) {
+				bool dquoteFound = (value.IndexOf ('\"') > -1);
+				bool squoteFound = (value.IndexOf ('\'') > -1);
+	
+				if (dquoteFound && squoteFound) {
+					builder.Append ('\"');
+					builder.Append (value.Replace ("\"", "\"\""));
+					builder.Append ('\"');
+				} else if (dquoteFound) {
+					builder.Append ('\'');
+					builder.Append (value);
+					builder.Append ('\'');
+				} else if (squoteFound || value.IndexOf ('=') > -1 || value.IndexOf (';') > -1) {
+					builder.Append ('\"');
+					builder.Append (value);
+					builder.Append ('\"');
+				} else if (ValueNeedsQuoting (value)) {
+					builder.Append ('\"');
+					builder.Append (value);
+					builder.Append ('\"');
+				} else
+					builder.Append (value);
+			} else {
+				int braces = 0;
+				bool semicolonFound = false;
+				int len = value.Length;
+				bool needBraces = false;
+
+				int lastChar = -1;
+
+				for (int i = 0; i < len; i++) {
+					int peek = 0;
+					if (i == (len - 1))
+						peek = -1;
+					else
+						peek = value [i + 1];
+
+					char c = value [i];
+					switch (c) {
+					case '{':
+						braces++;
+						break;
+					case '}':
+						if (peek.Equals (c)) {
+							i++;
+							continue;
+						} else {
+							braces--;
+							if (peek != -1)
+								needBraces = true;
+						}
+						break;
+					case ';':
+						semicolonFound = true;
+						break;
+					default:
+						break;
+					}
+					lastChar = c;
+				}
+
+				if (value [0] == '{' && (lastChar != '}' || (braces == 0 && needBraces))) {
+					builder.Append ('{');
+					builder.Append (value.Replace ("}", "}}"));
+					builder.Append ('}');
+					return;
+				}
+
+				bool isDriver = (string.Compare (keyword, "Driver", StringComparison.InvariantCultureIgnoreCase) == 0);
+				if (isDriver) {
+					if (value [0] == '{' && lastChar == '}' && !needBraces) {
+						builder.Append (value);
+						return;
+					}
+					builder.Append ('{');
+					builder.Append (value.Replace ("}", "}}"));
+					builder.Append ('}');
+					return;
+				}
+
+				if (value [0] == '{' && (braces != 0 || lastChar != '}') && needBraces) {
+					builder.Append ('{');
+					builder.Append (value.Replace ("}", "}}"));
+					builder.Append ('}');
+					return;
+				}
+
+				if (value [0] != '{' && semicolonFound) {
+					builder.Append ('{');
+					builder.Append (value.Replace ("}", "}}"));
+					builder.Append ('}');
+					return;
+				}
+
+				builder.Append (value);
+			}
+		}
+
+		public static void AppendKeyValuePair (StringBuilder builder, string keyword, string value)
+		{
+			AppendKeyValuePair (builder, keyword, value, false);
+		}
+
+		public virtual void Clear ()
+		{
+			_dictionary.Clear ();
+		}
+
+		public virtual bool ContainsKey (string keyword)
+		{
+			if (keyword == null)
+				throw new ArgumentNullException ("keyword");
+			return _dictionary.ContainsKey (keyword);
+		}
+
+                public virtual bool EquivalentTo (DbConnectionStringBuilder connectionStringBuilder)
+                {
+                        bool ret = true;
+                        try {
+                                if (Count != connectionStringBuilder.Count)
+                                        ret = false;
+                                else {
+                                        foreach (string key in Keys) {
+                                                if (!this [key].Equals (connectionStringBuilder [key])) {
+                                                        ret = false;
+                                                        break;
+                                                }
+                                        }
+                                }
+                        } catch (ArgumentException) {
+                                ret = false;
+                        }
+                        return ret;
+                }
+
+		[MonoTODO]
+		protected virtual void GetProperties (Hashtable propertyDescriptors)
+		{
+			throw new NotImplementedException ();
+		}
+
+		[MonoTODO]
+		protected internal void ClearPropertyDescriptors ()
+		{
+			throw new NotImplementedException ();
+		}
+
+		public virtual bool Remove (string keyword)
+		{
+			if (keyword == null)
+				throw new ArgumentNullException ("keyword");
+			return _dictionary.Remove (keyword);
+		}
+
+                public virtual bool ShouldSerialize (string keyword)
+                {
+                        throw new NotImplementedException ();
+                }
+
+                void ICollection.CopyTo (Array array, int index)
+                {
+			if (array == null)
+				throw new ArgumentNullException ("array");
+			KeyValuePair<string, object> [] arr = array as KeyValuePair<string, object> [];
+			if (arr == null)
+				throw new ArgumentException ("Target array type is not compatible with the type of items in the collection");
+			((ICollection<KeyValuePair<string, object>>) _dictionary).CopyTo (arr, index);
+                }
+
+                void IDictionary.Add (object keyword, object value)
+                {
+                        this.Add ((string) keyword, value);
+                }
+
+                bool IDictionary.Contains (object keyword)
+                {
+                        return ContainsKey ((string) keyword);
+                }
+
+                IDictionaryEnumerator IDictionary.GetEnumerator ()
+                {
+                        return (IDictionaryEnumerator) _dictionary.GetEnumerator ();
+                }
+
+                void IDictionary.Remove (object keyword)
+                {
+                        Remove ((string) keyword);
+                }
+
+                IEnumerator IEnumerable.GetEnumerator ()
+                {
+                        return (IEnumerator) _dictionary.GetEnumerator ();
+                }
+
+                private static object _staticAttributeCollection = null;
+                AttributeCollection ICustomTypeDescriptor.GetAttributes ()
+                {
+                        object value = _staticAttributeCollection;
+                        if (value == null) {
+                                CLSCompliantAttribute clsAttr = new CLSCompliantAttribute (true);
+                                DefaultMemberAttribute defMemAttr = new DefaultMemberAttribute ("Item");
+                                Attribute [] attrs = {clsAttr, defMemAttr};
+                                value = new AttributeCollection (attrs);
+                        }
+                        System.Threading.Interlocked.CompareExchange (ref _staticAttributeCollection, value, null);
+                        return _staticAttributeCollection as AttributeCollection;
+                }
+
+                string ICustomTypeDescriptor.GetClassName ()
+                {
+                        return this.GetType ().ToString ();
+                }
+
+                string ICustomTypeDescriptor.GetComponentName ()
+                {
+                        return null;
+                }
+
+                TypeConverter ICustomTypeDescriptor.GetConverter ()
+                {
+                        return new CollectionConverter ();
+                }
+
+                EventDescriptor ICustomTypeDescriptor.GetDefaultEvent ()
+                {
+                        return null;
+                }
+
+                PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty ()
+                {
+                        return null;
+                }
+
+                object ICustomTypeDescriptor.GetEditor (Type editorBaseType)
+                {
+                        return null;
+                }
+
+                EventDescriptorCollection ICustomTypeDescriptor.GetEvents ()
+                {
+                        return EventDescriptorCollection.Empty;
+                }
+
+                EventDescriptorCollection ICustomTypeDescriptor.GetEvents (Attribute [] attributes)
+                {
+                        return EventDescriptorCollection.Empty;
+                }
+
+                PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties ()
+                {
+                        return PropertyDescriptorCollection.Empty;
+                }
+
+                PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties (Attribute [] attributes)
+                {
+                        return PropertyDescriptorCollection.Empty;
+                }
+
+                object ICustomTypeDescriptor.GetPropertyOwner (PropertyDescriptor pd)
+                {
+                        throw new NotImplementedException ();
+                }
+
+                public override string ToString ()
+                {
+                        return ConnectionString;
+                }
+
+                public virtual bool TryGetValue (string keyword, out object value)
+                {
+                        // FIXME : not sure, difference between this [keyword] and this method
+                        bool found = ContainsKey (keyword);
+                        if (found)
+                                value = this [keyword];
+                        else
+                                value = null;
+                        return found;
+                }
+
+		static ArgumentException CreateInvalidKeywordException (string keyword)
+		{
+			return new ArgumentException ("A keyword cannot contain "
+				+ "control characters, leading semicolons or "
+				+ "leading or trailing whitespace.", keyword);
+		}
+
+		static ArgumentException CreateConnectionStringInvalidException (int index)
+		{
+			return new ArgumentException ("Format of initialization "
+				+ "string does not conform to specifications at "
+				+ "index " + index + ".");
+		}
+
+		static bool ValueNeedsQuoting (string value)
+		{
+			foreach (char c in value) {
+				if (char.IsWhiteSpace (c))
+					return true;
+			}
+			return false;
+		}
+		void ParseConnectionString (string connectionString)
+		{
+			if (useOdbcRules)
+				ParseConnectionStringOdbc (connectionString);
+			else
+				ParseConnectionStringNonOdbc (connectionString);
+		}
+
+		void ParseConnectionStringOdbc (string connectionString)
+		{
+			bool inQuote = false;
+			bool inDQuote = false;
+			bool inName = true;
+			bool inBraces = false;
+
+			string name = String.Empty;
+			string val = String.Empty;
+			StringBuilder sb = new StringBuilder ();
+			int len = connectionString.Length;
+
+			for (int i = 0; i < len; i++) {
+				char c = connectionString [i];
+				int peek = (i == (len - 1)) ? -1 : connectionString [i + 1];
+
+				switch (c) {
+				case '{':
+					if (inName) {
+						sb.Append (c);
+						continue;
+					}
+
+					if (sb.Length == 0)
+						inBraces = true;
+					sb.Append (c);
+					break;
+				case '}':
+					if (inName || !inBraces) {
+						sb.Append (c);
+						continue;
+					}
+
+					if (peek == -1) {
+						sb.Append (c);
+						inBraces = false;
+					} else if (peek.Equals (c)) {
+						sb.Append (c);
+						sb.Append (c);
+						i++;
+					} else {
+						int next = NextNonWhitespaceChar (connectionString, i);
+						if (next != -1 && ((char) next) != ';')
+							throw CreateConnectionStringInvalidException (next);
+						sb.Append (c);
+						inBraces = false;
+					}
+					break;
+				case ';':
+					if (inName || inBraces) {
+						sb.Append (c);
+						continue;
+					}
+
+					if (name.Length > 0 && sb.Length > 0) {
+						val = sb.ToString ();
+						name = name.ToLower ().TrimEnd ();
+						this [name] = val;
+					} else if (sb.Length > 0)
+						throw CreateConnectionStringInvalidException (c);
+					inName = true;
+					name = String.Empty;
+					sb.Length = 0;
+					break;
+				case '=':
+					if (inBraces || !inName) {
+						sb.Append (c);
+						continue;
+					}
+
+					name = sb.ToString ();
+					if (name.Length == 0)
+						throw CreateConnectionStringInvalidException (c);
+					sb.Length = 0;
+					inName = false;
+					break;
+				default:
+					if (inDQuote || inQuote || inBraces)
+						sb.Append (c);
+					else if (char.IsWhiteSpace (c)) {
+						// ignore leading whitespace
+						if (sb.Length > 0) {
+							int nextChar = SkipTrailingWhitespace (connectionString, i);
+							if (nextChar == -1)
+								sb.Append (c);
+							else
+								i = nextChar;
+						}
+					} else
+						sb.Append (c);
+					break;
+				}
+			}
+
+			if ((inName && sb.Length > 0) || inDQuote || inQuote || inBraces)
+				throw CreateConnectionStringInvalidException (len - 1);
+
+			if (name.Length > 0 && sb.Length > 0) {
+				val = sb.ToString ();
+				name = name.ToLower ().TrimEnd ();
+				this [name] = val;
+			}
+		}
+
+		void ParseConnectionStringNonOdbc (string connectionString)
+		{
+			bool inQuote = false;
+			bool inDQuote = false;
+			bool inName = true;
+
+			string name = String.Empty;
+			string val = String.Empty;
+			StringBuilder sb = new StringBuilder ();
+			int len = connectionString.Length;
+
+			for (int i = 0; i < len; i++) {
+				char c = connectionString [i];
+				int peek = (i == (len - 1)) ? -1 : connectionString [i + 1];
+
+				switch (c) {
+				case '\'':
+					if (inName) {
+						sb.Append (c);
+						continue;
+					}
+
+					if (inDQuote)
+						sb.Append (c);
+					else if (inQuote) {
+						if (peek == -1)
+							inQuote = false;
+						else if (peek.Equals (c)) {
+							sb.Append (c);
+							i++;
+						} else {
+							int next = NextNonWhitespaceChar (connectionString, i);
+							if (next != -1 && ((char) next) != ';')
+								throw CreateConnectionStringInvalidException (next);
+							inQuote = false;
+						}
+
+						if (!inQuote) {
+							val = sb.ToString ();
+							name = name.ToLower ().TrimEnd ();
+							this [name] = val;
+							inName = true;
+							name = String.Empty;
+							sb.Length = 0;
+						}
+					} else if (sb.Length == 0)
+						inQuote = true;
+					else
+						sb.Append (c);
+					break;
+				case '"':
+					if (inName) {
+						sb.Append (c);
+						continue;
+					}
+
+					if (inQuote)
+						sb.Append (c);
+					else if (inDQuote) {
+						if (peek == -1)
+							inDQuote = false;
+						else if (peek.Equals (c)) {
+							sb.Append (c);
+							i++;
+						} else {
+							int next = NextNonWhitespaceChar (connectionString, i);
+							if (next != -1 && ((char) next) != ';')
+								throw CreateConnectionStringInvalidException (next);
+							inDQuote = false;
+						}
+					} else if (sb.Length == 0)
+						inDQuote = true;
+					else
+						sb.Append (c);
+					break;
+				case ';':
+					if (inName) {
+						sb.Append (c);
+						continue;
+					}
+
+					if (inDQuote || inQuote)
+						sb.Append (c);
+					else {
+						if (name.Length > 0 && sb.Length > 0) {
+							val = sb.ToString ();
+							name = name.ToLower ().TrimEnd ();
+							this [name] = val;
+						} else if (sb.Length > 0)
+							throw CreateConnectionStringInvalidException (c);
+						inName = true;
+						name = String.Empty;
+						sb.Length = 0;
+					}
+					break;
+				case '=':
+					if (inDQuote || inQuote || !inName)
+						sb.Append (c);
+					else if (peek != -1 && peek.Equals (c)) {
+						sb.Append (c);
+						i++;
+					} else {
+						name = sb.ToString ();
+						if (name.Length == 0)
+							throw CreateConnectionStringInvalidException (c);
+						sb.Length = 0;
+						inName = false;
+					}
+					break;
+				default:
+					if (inDQuote || inQuote)
+						sb.Append (c);
+					else if (char.IsWhiteSpace (c)) {
+						// ignore leading whitespace
+						if (sb.Length > 0) {
+							int nextChar = SkipTrailingWhitespace (connectionString, i);
+							if (nextChar == -1)
+								sb.Append (c);
+							else
+								i = nextChar;
+						}
+					} else
+						sb.Append (c);
+					break;
+				}
+			}
+
+			if ((inName && sb.Length > 0) || inDQuote || inQuote)
+				throw CreateConnectionStringInvalidException (len -1);
+
+			if (name.Length > 0 && sb.Length > 0) {
+				val = sb.ToString ();
+				name = name.ToLower ().TrimEnd ();
+				this [name] = val;
+			}
+		}
+
+		static int SkipTrailingWhitespace (string value, int index)
+		{
+			int len = value.Length;
+			for (int i = (index + 1); i < len; i++) {
+				char c = value [i];
+				if (c == ';')
+					return (i - 1);
+				if (!char.IsWhiteSpace (c))
+					return -1;
+			}
+			return len - 1;
+		}
+
+		static int NextNonWhitespaceChar (string value, int index)
+		{
+			int len = value.Length;
+			for (int i = (index + 1); i < len; i++) {
+				char c = value [i];
+				if (!char.IsWhiteSpace (c))
+					return (int) c;
+			}
+			return -1;
+		}
+
+		#endregion // Public Methods
+	}
+}
+#endif // NET_2_0 using
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DbDataPermission.cs
@@ -0,0 +1,317 @@
+//
+// System.Data.Common.DbDataPermission.cs
+//
+// Authors:
+//	Rodrigo Moya (rodrigo@ximian.com)
+//	Tim Coleman (tim@timcoleman.com)
+//	Sebastien Pouliot  <sebastien@ximian.com>
+//
+// (C) Ximian, Inc
+// Copyright (C) Tim Coleman, 2002-2003
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Collections;
+using System.Security;
+using System.Security.Permissions;
+
+namespace System.Data.Common {
+
+	[Serializable]
+	public abstract class DBDataPermission : CodeAccessPermission, IUnrestrictedPermission {
+
+		#region Fields
+
+		private const int version = 1;
+
+		private bool allowBlankPassword;
+		private PermissionState state;
+		private Hashtable _connections;
+
+		#endregion // Fields
+
+		#region Constructors
+
+#if NET_2_0
+		[Obsolete ("use DBDataPermission (PermissionState.None)", true)]
+#endif
+		protected DBDataPermission () 
+			: this (PermissionState.None)
+		{
+		}
+
+		protected DBDataPermission (DBDataPermission permission)
+		{
+			if (permission == null)
+				throw new ArgumentNullException ("permission");
+
+			state = permission.state;
+			if (state != PermissionState.Unrestricted) {
+				allowBlankPassword = permission.allowBlankPassword;
+				_connections = (Hashtable) permission._connections.Clone ();
+			}
+		}
+
+		protected DBDataPermission (DBDataPermissionAttribute permissionAttribute)
+		{
+			if (permissionAttribute == null)
+				throw new ArgumentNullException ("permissionAttribute");
+
+			_connections = new Hashtable ();
+			if (permissionAttribute.Unrestricted) {
+				state = PermissionState.Unrestricted;
+			}
+			else {
+				state = PermissionState.None;
+				allowBlankPassword = permissionAttribute.AllowBlankPassword;
+				if (permissionAttribute.ConnectionString.Length > 0) {
+					Add (permissionAttribute.ConnectionString, permissionAttribute.KeyRestrictions, 
+						permissionAttribute.KeyRestrictionBehavior);
+				}
+			}
+		}
+
+		protected DBDataPermission (PermissionState state) 
+		{
+			this.state = PermissionHelper.CheckPermissionState (state, true);
+			_connections = new Hashtable ();
+		}
+
+#if NET_2_0
+		[Obsolete ("use DBDataPermission (PermissionState.None)", true)]
+		protected 
+#else
+		public
+#endif
+		DBDataPermission (PermissionState state, bool allowBlankPassword)
+			: this (state)
+		{
+			this.allowBlankPassword = allowBlankPassword;
+		}
+
+		#endregion // Constructors
+
+		#region Properties
+
+		public bool AllowBlankPassword {
+			get { return allowBlankPassword; }
+			set { allowBlankPassword = value; }
+		}
+		
+		#endregion // Properties
+
+		#region Methods
+
+		public virtual void Add (string connectionString, string restrictions, KeyRestrictionBehavior behavior)
+		{
+			state = PermissionState.None;
+			_connections [connectionString] = new object [2] { restrictions, behavior };
+		}
+
+		protected void Clear ()
+		{
+			_connections.Clear ();
+		}
+
+		public override IPermission Copy () 
+		{
+			DBDataPermission dbdp = CreateInstance ();
+			dbdp.allowBlankPassword = this.allowBlankPassword;
+			dbdp._connections = (Hashtable) this._connections.Clone ();
+			return dbdp;
+		}
+
+		protected virtual DBDataPermission CreateInstance ()
+		{
+			return (DBDataPermission) Activator.CreateInstance (this.GetType (), new object [1] { PermissionState.None });
+		}
+
+		public override void FromXml (SecurityElement securityElement) 
+		{
+			PermissionHelper.CheckSecurityElement (securityElement, "securityElement", version, version);
+			// Note: we do not (yet) care about the return value 
+			// as we only accept version 1 (min/max values)
+
+			state = (PermissionHelper.IsUnrestricted (securityElement) ? 
+				PermissionState.Unrestricted : PermissionState.None);
+
+			allowBlankPassword = false;
+			string blank = securityElement.Attribute ("AllowBlankPassword");
+			if (blank != null) {
+#if NET_2_0
+				// avoid possible exceptions with Fx 2.0
+				if (!Boolean.TryParse (blank, out allowBlankPassword))
+					allowBlankPassword = false;
+#else
+				try {
+					allowBlankPassword = Boolean.Parse (blank);
+				}
+				catch {
+					allowBlankPassword = false;
+				}
+#endif
+			}
+
+			if (securityElement.Children != null) {
+				foreach (SecurityElement child in securityElement.Children) {
+					string connect = child.Attribute ("ConnectionString");
+					string restricts = child.Attribute ("KeyRestrictions");
+					KeyRestrictionBehavior behavior = (KeyRestrictionBehavior) Enum.Parse (
+						typeof (KeyRestrictionBehavior), child.Attribute ("KeyRestrictionBehavior"));
+
+					if ((connect != null) && (connect.Length > 0))
+						Add (connect, restricts, behavior);
+				}
+			}
+		}
+
+		public override IPermission Intersect (IPermission target) 
+		{
+			// FIXME: restrictions not completely implemented - nor documented
+			DBDataPermission dbdp = Cast (target);
+			if (dbdp == null)
+				return null;
+			if (IsUnrestricted ()) {
+				if (dbdp.IsUnrestricted ()) {
+					DBDataPermission u = CreateInstance ();
+					u.state = PermissionState.Unrestricted;
+					return u;
+				}
+				return dbdp.Copy ();
+			}
+			if (dbdp.IsUnrestricted ())
+				return Copy ();
+			if (IsEmpty () || dbdp.IsEmpty ())
+				return null;
+
+			DBDataPermission p = CreateInstance ();
+			p.allowBlankPassword = (allowBlankPassword && dbdp.allowBlankPassword);
+			foreach (DictionaryEntry de in _connections) {
+				object o = dbdp._connections [de.Key];
+				if (o != null)
+					p._connections.Add (de.Key, de.Value);
+			}
+			return (p._connections.Count > 0) ? p : null;
+		}
+
+		public override bool IsSubsetOf (IPermission target) 
+		{
+			// FIXME: restrictions not completely implemented - nor documented
+			DBDataPermission dbdp = Cast (target);
+			if (dbdp == null)
+				return IsEmpty ();
+			if (dbdp.IsUnrestricted ())
+				return true;
+			if (IsUnrestricted ())
+				return dbdp.IsUnrestricted ();
+
+			if (allowBlankPassword && !dbdp.allowBlankPassword)
+				return false;
+			if (_connections.Count > dbdp._connections.Count)
+				return false;
+
+			foreach (DictionaryEntry de in _connections) {
+				object o = dbdp._connections [de.Key];
+				if (o == null)
+					return false;
+				// FIXME: this is a subset of what is required
+				// it seems that we must process both the connect string
+				// and the restrictions - but this has other effects :-/
+			}
+			return true;
+		}
+
+		public bool IsUnrestricted () 
+		{
+			return (state == PermissionState.Unrestricted);
+		}
+
+		public override SecurityElement ToXml ()
+		{
+			SecurityElement se = PermissionHelper.Element (this.GetType (), version);
+			if (IsUnrestricted ()) {
+				se.AddAttribute ("Unrestricted", "true");
+			}
+			else {
+				// attribute is present for both True and False
+				se.AddAttribute ("AllowBlankPassword", allowBlankPassword.ToString ());
+				foreach (DictionaryEntry de in _connections) {
+					SecurityElement child = new SecurityElement ("add");
+					child.AddAttribute ("ConnectionString", (string) de.Key);
+					object[] restrictionsInfo = (object[]) de.Value;
+					child.AddAttribute ("KeyRestrictions", (string) restrictionsInfo [0]);
+					KeyRestrictionBehavior krb = (KeyRestrictionBehavior) restrictionsInfo [1];
+					child.AddAttribute ("KeyRestrictionBehavior", krb.ToString ());
+					se.AddChild (child);
+				}
+			}
+			return se;
+		}
+
+		public override IPermission Union (IPermission target) 
+		{
+			// FIXME: restrictions not completely implemented - nor documented
+			DBDataPermission dbdp = Cast (target);
+			if (dbdp == null)
+				return Copy ();
+			if (IsEmpty () && dbdp.IsEmpty ())
+				return Copy ();
+
+			DBDataPermission p = CreateInstance ();
+			if (IsUnrestricted () || dbdp.IsUnrestricted ()) {
+				p.state = PermissionState.Unrestricted;
+			}
+			else {
+				p.allowBlankPassword = (allowBlankPassword || dbdp.allowBlankPassword);
+				p._connections = new Hashtable (_connections.Count + dbdp._connections.Count);
+				foreach (DictionaryEntry de in _connections)
+					p._connections.Add (de.Key, de.Value);
+				// don't duplicate
+				foreach (DictionaryEntry de in dbdp._connections)
+					p._connections [de.Key] = de.Value;
+			}
+			return p;
+		}
+
+		// helpers
+
+		private bool IsEmpty ()
+		{
+			return ((state != PermissionState.Unrestricted) && (_connections.Count == 0));
+		}
+
+		private DBDataPermission Cast (IPermission target)
+		{
+			if (target == null)
+				return null;
+
+			DBDataPermission dbdp = (target as DBDataPermission);
+			if (dbdp == null) {
+				PermissionHelper.ThrowInvalidPermission (target, this.GetType ());
+			}
+
+			return dbdp;
+		}
+
+		#endregion // Methods
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Common/DbDataPermissionAttribute.cs
@@ -0,0 +1,113 @@
+//
+// System.Data.Common.DbDataPermissionAttribute.cs
+//
+// Authors:
+//	Rodrigo Moya (rodrigo@ximian.com)
+//	Tim Coleman (tim@timcoleman.com)
+//	Sebastien Pouliot  <sebastien@ximian.com>
+//
+// (C) Ximian, Inc
+// Copyright (C) Tim Coleman, 2002
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.ComponentModel;
+using System.Security.Permissions;
+using System.Globalization;
+
+namespace System.Data.Common {
+
+	[AttributeUsage (AttributeTargets.Assembly | AttributeTargets.Class | 
+			 AttributeTargets.Struct | AttributeTargets.Constructor | 
+			 AttributeTargets.Method, AllowMultiple=true, Inherited=false)]
+	[Serializable]
+	public abstract class DBDataPermissionAttribute : CodeAccessSecurityAttribute {
+		#region Fields
+
+		bool allowBlankPassword;
+		string keyRestrictions;
+		KeyRestrictionBehavior keyRestrictionBehavior;
+		string connectionString;
+
+		#endregion // Fields
+
+		#region Constructors
+
+		protected DBDataPermissionAttribute (SecurityAction action) 
+			: base (action) 
+		{
+		}
+
+		#endregion // Constructors
+
+		#region Properties
+
+		public bool AllowBlankPassword {
+			get { return allowBlankPassword; }
+			set { allowBlankPassword = value; }
+		}
+
+		public string KeyRestrictions {
+			get {
+				if (keyRestrictions == null)
+					return String.Empty;
+				return keyRestrictions;
+			}
+			set { keyRestrictions = value; }
+		}
+
+		public string ConnectionString {
+			get {
+				if (connectionString == null)
+					return String.Empty;
+				return connectionString;
+			}
+			set { connectionString = value; }
+		}
+
+		public KeyRestrictionBehavior KeyRestrictionBehavior {
+			get { return keyRestrictionBehavior; }
+			set {
+				ExceptionHelper.CheckEnumValue (typeof (KeyRestrictionBehavior), value);
+				keyRestrictionBehavior = value;
+			}
+		}
+
+		#endregion // Properties
+
+		#region // Methods
+		[EditorBrowsableAttribute (EditorBrowsableState.Never)]
+		public bool ShouldSerializeConnectionString ()
+		{
+			// FIXME: configurable ? why is this in the attribute class ?
+			return false;
+		}
+
+		[EditorBrowsableAttribute (EditorBrowsableState.Never)]
+		public bool ShouldSerializeKeyRestrictions ()
+		{
+			// FIXME: configurable ? why is this in the attribute class ?
+			return false;
+		}
+		#endregion // Methods
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Odbc/OdbcConnectionStringBuilder.cs
@@ -0,0 +1,219 @@
+//
+// System.Data.Odbc.OdbcConnectionStringBuilder
+//
+// Authors: 
+//	  Nidhi Rawal (rawalnidhi_rawal@yahoo.com)
+//
+// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if NET_2_0
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Data.Odbc;
+using System.Reflection;
+using System.Text;
+
+namespace System.Data.Odbc
+{
+	[DefaultProperty ("Driver")]
+	[TypeConverter ("System.Data.Odbc.OdbcConnectionStringBuilder+OdbcConnectionStringBuilderConverter, " + Consts.AssemblySystem_Data)]
+	public sealed class OdbcConnectionStringBuilder : DbConnectionStringBuilder
+	{
+		#region Fields
+		string driver;
+		string dsn;
+		#endregion //Fields
+
+		#region Constructors
+
+		public OdbcConnectionStringBuilder () : base (true)
+		{
+		}
+
+		public OdbcConnectionStringBuilder (string connectionString) : base (true)
+		{
+			if (connectionString == null) {
+				base.ConnectionString = string.Empty;
+				return;
+			}
+
+			base.ConnectionString = connectionString;
+		}
+
+		#endregion // Constructors
+
+		#region Properties
+
+		public override Object this [string keyword] {
+			get {
+				if (keyword == null)
+					throw new ArgumentNullException ("keyword");
+				if (string.Compare (keyword, "Driver", StringComparison.InvariantCultureIgnoreCase) == 0)
+					return Driver;
+				if (string.Compare (keyword, "Dsn", StringComparison.InvariantCultureIgnoreCase) == 0)
+					return Dsn;
+				return base [keyword];
+			}
+			set {
+				if (value == null) {
+					Remove (keyword);
+					return;
+				}
+
+				if (keyword == null)
+					throw new ArgumentNullException ("keyword");
+
+				string text_value = value.ToString ();
+
+				if (string.Compare (keyword, "Driver", StringComparison.InvariantCultureIgnoreCase) == 0) {
+					Driver = text_value;
+					return;
+				} else if (string.Compare (keyword, "Dsn", StringComparison.InvariantCultureIgnoreCase) == 0) {
+					dsn = text_value;
+				} else if (value.ToString ().IndexOf (';') != -1) {
+					text_value = "{" + text_value + "}";
+				}
+				base [keyword] = value;
+			}
+		}
+
+		public override ICollection Keys {
+			get {
+				List<string> keys = new List<string> ();
+				keys.Add ("Dsn");
+				keys.Add ("Driver");
+
+				ICollection base_keys = base.Keys;
+				foreach (string keyword in base_keys) {
+					if (string.Compare (keyword, "Driver", StringComparison.InvariantCultureIgnoreCase) == 0)
+						continue;
+					if (string.Compare (keyword, "Dsn", StringComparison.InvariantCultureIgnoreCase) == 0)
+						continue;
+					keys.Add (keyword);
+				}
+
+				string [] final = new string [keys.Count];
+				keys.CopyTo (final);
+				return final;
+			}
+		}
+
+		[DisplayName ("Driver")]
+		[RefreshProperties (RefreshProperties.All)]
+		public string Driver {
+			get {
+				if (driver == null)
+					return string.Empty;
+				return driver;
+			}
+			set {
+				if (value == null)
+					throw new ArgumentNullException ("Driver");
+				driver = value;
+
+				if (value.Length > 0) {
+					int startBrace = value.IndexOf ('{');
+					int endBrace = value.IndexOf ('}');
+					if (startBrace == -1 || endBrace == -1)
+						value = "{" + value + "}";
+					else if (startBrace > 0 || endBrace < (value.Length - 1))
+						value = "{" + value + "}";
+				}
+				base ["Driver"] = value;
+			}
+		}
+		
+		[DisplayName ("Dsn")]
+		[RefreshProperties (RefreshProperties.All)]
+		public string Dsn {
+			get {
+				if (dsn == null)
+					return string.Empty;
+				return dsn;
+			}
+			set {
+				if (value == null)
+					throw new ArgumentNullException ("Dsn");
+				dsn = value;
+				base ["Dsn"] = dsn;
+			}
+		}
+
+		#endregion // Properties
+
+		#region Methods
+
+		public override bool ContainsKey (string keyword)
+		{
+			if (keyword == null)
+				throw new ArgumentNullException ("keyword");
+			if (string.Compare (keyword, "Driver", StringComparison.InvariantCultureIgnoreCase) == 0)
+				return true;
+			if (string.Compare (keyword, "Dsn", StringComparison.InvariantCultureIgnoreCase) == 0)
+				return true;
+			return base.ContainsKey (keyword);
+		}
+
+		public override bool Remove (string keyword)
+		{
+			if (keyword == null)
+				throw new ArgumentNullException ("keyword");
+			if (string.Compare (keyword, "Driver", StringComparison.InvariantCultureIgnoreCase) == 0)
+				driver = string.Empty;
+			else if (string.Compare (keyword, "Dsn", StringComparison.InvariantCultureIgnoreCase) == 0)
+				dsn = string.Empty;
+			return base.Remove (keyword);
+		}
+
+		public override void Clear ()
+		{
+			driver = null;
+			dsn = null;
+			base.Clear ();
+		}
+
+		public override bool TryGetValue (string keyword, out Object value)
+		{
+			if (keyword == null )
+				throw new ArgumentNullException ("keyword");
+			bool found = base.TryGetValue (keyword, out value);
+			if (found)
+				return found;
+			if (string.Compare (keyword, "Driver", StringComparison.InvariantCultureIgnoreCase) == 0) {
+				value = string.Empty;
+				return true;
+			} else if (string.Compare (keyword, "Dsn", StringComparison.InvariantCultureIgnoreCase) == 0) {
+				value = string.Empty;
+				return true;
+			}
+			return false;
+		}
+
+		#endregion // Methods
+	}
+}
+#endif // NET_2_0 using
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Odbc/OdbcError.cs
@@ -0,0 +1,104 @@
+//
+// System.Data.Odbc.OdbcError
+//
+// Author:
+//   Brian Ritchie (brianlritchie@hotmail.com) 
+//
+// Copyright (C) Brian Ritchie, 2002
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Collections;
+using System.ComponentModel;
+using System.Data;
+using System.Data.Common;
+using System.Text;
+
+namespace System.Data.Odbc
+{
+	[Serializable]
+	public sealed class OdbcError
+	{
+		readonly string _message;
+		string _source;
+		readonly string _state;
+		readonly int _nativeerror;
+
+		#region Constructors
+
+		internal OdbcError (OdbcConnection connection)
+		{
+			_nativeerror = 1;
+			_source = connection.SafeDriver;
+			_message = "Error in " + _source;
+			_state = string.Empty;
+		}
+
+		internal OdbcError (string message, string state, int nativeerror)
+		{
+			_message = message;
+			_state = state;
+			_nativeerror = nativeerror;
+		}
+
+		#endregion // Constructors
+		
+		#region Properties
+
+		public string Message {
+			get { return _message; }
+		}
+
+		public int NativeError {
+			get { return _nativeerror; }
+		}
+
+		public string Source {
+			get { return _source; }
+		}
+
+		public string SQLState {
+			get { return _state; }
+		}
+
+		#endregion // Properties
+		
+		#region methods
+		
+		public override string ToString ()
+		{
+			return Message;
+		}
+
+		internal void SetSource (string source)
+		{
+			_source = source;
+		}
+
+		#endregion
+
+	}
+
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Odbc/OdbcPermission.cs
@@ -0,0 +1,87 @@
+//
+// System.Data.Odbc.OdbcPermission
+//
+// Authors:
+//	Umadevi S (sumadevi@novell.com)
+//	Sebastien Pouliot  <sebastien@ximian.com>
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Data.Common;
+using System.Security;
+using System.Security.Permissions;
+
+namespace System.Data.Odbc
+{
+	[Serializable]
+	public sealed class OdbcPermission : DBDataPermission
+	{
+		#region Constructors
+
+		[Obsolete ("use OdbcPermission(PermissionState.None)", true)]
+		public OdbcPermission ()
+			: base (PermissionState.None)
+		{
+		}
+
+		public OdbcPermission (PermissionState state)
+			: base (state)
+		{
+		}
+
+		[Obsolete ("use OdbcPermission(PermissionState.None)", true)]
+		public OdbcPermission (PermissionState state, bool allowBlankPassword)
+			: base (state)
+		{
+			AllowBlankPassword = allowBlankPassword;
+		}
+
+		// required for Copy method
+		internal OdbcPermission (DBDataPermission permission)
+			: base (permission)
+		{
+		}
+
+		// easier (and common) permission creation from attribute class
+		internal OdbcPermission (DBDataPermissionAttribute attribute)
+			: base (attribute)
+		{
+		}
+
+		#endregion
+
+		#region Methods
+
+		public override IPermission Copy ()
+		{
+			return new OdbcPermission (this);
+		}
+
+		public override void Add (string connectionString, string restrictions, KeyRestrictionBehavior behavior)
+		{
+			base.Add (connectionString, restrictions, behavior);
+		}
+
+		#endregion
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data.Odbc/OdbcPermissionAttribute.cs
@@ -0,0 +1,64 @@
+//
+// System.Data.Odbc.OdbcPermissionAttribute
+//
+// Authors:
+//	Umadevi S (sumadevi@novell.com)
+//	Sebastien Pouliot  <sebastien@ximian.com>
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Data.Common;
+using System.Security;
+using System.Security.Permissions;
+
+namespace System.Data.Odbc {
+
+	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | 
+			AttributeTargets.Struct | AttributeTargets.Constructor |
+			AttributeTargets.Method, AllowMultiple=true,
+			Inherited=false)]
+	[Serializable]
+	public sealed class OdbcPermissionAttribute : DBDataPermissionAttribute {
+
+		#region Constructors 
+
+		public OdbcPermissionAttribute (SecurityAction action) 
+			: base (action)
+		{
+		}
+
+		#endregion
+
+		#region Properties
+		#endregion
+
+		#region Methods
+
+		public override IPermission CreatePermission () 
+		{
+			return new OdbcPermission (this);
+		}
+
+		#endregion
+	}
+}
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/System.Data/KeyRestrictionBehavior.cs
@@ -0,0 +1,41 @@
+//
+// System.Data.KeyRestrictionBehavior
+//
+// Author:
+//     Tim Coleman <tim@timcoleman.com>
+//
+// Copyright (C) Tim Coleman, 2003
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace System.Data {
+	[Serializable]
+	public enum KeyRestrictionBehavior
+	{
+		AllowOnly,
+		PreventUsage
+	}
+}
+
--- mono-4.2.1.102+dfsg2.orig/mcs/class/System.Data/Test/System.Data.Common/DBDataPermissionTest.cs
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/Test/System.Data.Common/DBDataPermissionTest.cs
@@ -500,7 +500,7 @@ namespace MonoTests.System.Data.Common {
 		{
 			NonAbstractDBDataPermission empty = new NonAbstractDBDataPermission (PermissionState.None);
 			NonAbstractDBDataPermission union = (NonAbstractDBDataPermission) empty.Union (empty);
-			Assert.IsNull (union, "Empty U Empty");
+			Assert.IsNotNull (union, "Empty U Empty");
 
 			NonAbstractDBDataPermission dbdp1 = new NonAbstractDBDataPermission (PermissionState.None);
 			dbdp1.Add (defaultConnectString, String.Empty, KeyRestrictionBehavior.AllowOnly);
--- mono-4.2.1.102+dfsg2.orig/mcs/class/System.Data/Test/System.Data.Common/DbConnectionStringBuilderTest.cs
+++ mono-4.2.1.102+dfsg2/mcs/class/System.Data/Test/System.Data.Common/DbConnectionStringBuilderTest.cs
@@ -1798,7 +1798,7 @@ namespace MonoTests.System.Data.Common
                         Assert.AreEqual (builder.GetType ().ToString (), className, "#1");
 
                         AttributeCollection collection = ictd.GetAttributes ();
-                        Assert.AreEqual (1, collection.Count, "#2");
+                        Assert.AreEqual (2, collection.Count, "#2");
                         object [] attr = builder.GetType ().GetCustomAttributes (typeof (DefaultMemberAttribute), false);
                         if (attr.Length > 0) {
                                 DefaultMemberAttribute defAtt = (DefaultMemberAttribute) attr [0];
--- mono-4.2.1.102+dfsg2.orig/mcs/class/System.ServiceModel/Makefile
+++ mono-4.2.1.102+dfsg2/mcs/class/System.ServiceModel/Makefile
@@ -32,8 +32,10 @@ ifeq (4, $(FRAMEWORK_VERSION_MAJOR))
 ifndef NO_SYSTEM_SERVICEMODEL_ACTIVATION_DEPENDENCY
 activation = $(the_libdir_base)System.ServiceModel.Activation.dll
 servicemodel_deps = $(activation)
+endif
 
 ifneq (plainservice/,$(intermediate))
+ifndef NO_SYSTEM_SERVICEMODEL_ACTIVATION_DEPENDENCY
 LIB_MCS_FLAGS += -define:HAS_ACTIVATION -r:System.ServiceModel.Activation.dll
 endif 
 endif # NO_SYSTEM_SERVICEMODEL_ACTIVATION_DEPENDENCY
--- mono-4.2.1.102+dfsg2.orig/mcs/class/reference-assemblies/Makefile
+++ mono-4.2.1.102+dfsg2/mcs/class/reference-assemblies/Makefile
@@ -10,10 +10,7 @@ install-local:
 	$(MKINSTALLDIRS) $(PROFILE_DIR)/2.0
 	$(MKINSTALLDIRS) $(PROFILE_DIR)/3.5
 	$(MKINSTALLDIRS) $(PROFILE_DIR)/4.0
-	$(INSTALL_LIB) ../../../external/binary-reference-assemblies/v2.0/* $(PROFILE_DIR)/2.0
-	$(INSTALL_LIB) ../../../external/binary-reference-assemblies/v3.5/* $(PROFILE_DIR)/3.5
-	$(INSTALL_LIB) ../../../external/binary-reference-assemblies/v4.0/* $(PROFILE_DIR)/4.0
 
-DISTFILES = $(wildcard ../../../external/binary-reference-assemblies/v4.0/*) $(wildcard ../../../external/binary-reference-assemblies/v3.5/*) $(wildcard ../../../external/binary-reference-assemblies/v2.0/*) Makefile
+DISTFILES = Makefile
 
 dist-local: dist-default
--- mono-4.2.1.102+dfsg2.orig/mcs/tools/xbuild/data/12.0/Microsoft.Common.targets
+++ mono-4.2.1.102+dfsg2/mcs/tools/xbuild/data/12.0/Microsoft.Common.targets
@@ -33,7 +33,7 @@
 		<WarningLevel Condition="'$(WarningLevel)' == ''" >2</WarningLevel>
 
 		<TargetFrameworkIdentifier Condition="'$(TargetFrameworkIdentifier)' == ''">.NETFramework</TargetFrameworkIdentifier>
-		<TargetFrameworkVersion Condition="'$(TargetFrameworkVersion)' == ''">v4.0</TargetFrameworkVersion>
+		<TargetFrameworkVersion Condition="'$(TargetFrameworkVersion)' == ''">v4.5</TargetFrameworkVersion>
 
 		<TargetFrameworkMoniker Condition="'$(TargetFrameworkMoniker)' == '' and '$(TargetFrameworkProfile)' != ''">$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion),Profile=$(TargetFrameworkProfile)</TargetFrameworkMoniker>
 	        <TargetFrameworkMoniker Condition="'$(TargetFrameworkMoniker)' == ''">$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion)</TargetFrameworkMoniker>
--- mono-4.2.1.102+dfsg2.orig/mcs/tools/xbuild/data/14.0/Microsoft.Common.targets
+++ mono-4.2.1.102+dfsg2/mcs/tools/xbuild/data/14.0/Microsoft.Common.targets
@@ -33,7 +33,7 @@
 		<WarningLevel Condition="'$(WarningLevel)' == ''" >2</WarningLevel>
 
 		<TargetFrameworkIdentifier Condition="'$(TargetFrameworkIdentifier)' == ''">.NETFramework</TargetFrameworkIdentifier>
-		<TargetFrameworkVersion Condition="'$(TargetFrameworkVersion)' == ''">v4.0</TargetFrameworkVersion>
+		<TargetFrameworkVersion Condition="'$(TargetFrameworkVersion)' == ''">v4.5</TargetFrameworkVersion>
 
 		<TargetFrameworkMoniker Condition="'$(TargetFrameworkMoniker)' == '' and '$(TargetFrameworkProfile)' != ''">$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion),Profile=$(TargetFrameworkProfile)</TargetFrameworkMoniker>
 	        <TargetFrameworkMoniker Condition="'$(TargetFrameworkMoniker)' == ''">$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion)</TargetFrameworkMoniker>
--- mono-4.2.1.102+dfsg2.orig/mcs/tools/xbuild/data/4.0/Microsoft.Common.targets
+++ mono-4.2.1.102+dfsg2/mcs/tools/xbuild/data/4.0/Microsoft.Common.targets
@@ -33,7 +33,7 @@
 		<WarningLevel Condition="'$(WarningLevel)' == ''" >2</WarningLevel>
 
 		<TargetFrameworkIdentifier Condition="'$(TargetFrameworkIdentifier)' == ''">.NETFramework</TargetFrameworkIdentifier>
-		<TargetFrameworkVersion Condition="'$(TargetFrameworkVersion)' == ''">v4.0</TargetFrameworkVersion>
+		<TargetFrameworkVersion Condition="'$(TargetFrameworkVersion)' == ''">v4.5</TargetFrameworkVersion>
 
 		<TargetFrameworkMoniker Condition="'$(TargetFrameworkMoniker)' == '' and '$(TargetFrameworkProfile)' != ''">$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion),Profile=$(TargetFrameworkProfile)</TargetFrameworkMoniker>
 	        <TargetFrameworkMoniker Condition="'$(TargetFrameworkMoniker)' == ''">$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion)</TargetFrameworkMoniker>
--- /dev/null
+++ mono-4.2.1.102+dfsg2/mono/arch/arm64/arm64-codegen.h
@@ -0,0 +1,851 @@
+/*
+ * arm64-codegen.h: ARM64 code generation macros
+ *
+ * Author:
+ *   Zoltan Varga (vargaz@gmail.com)
+ *
+ * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
+ */
+
+#ifndef __ARM64_CODEGEN_H__
+#define __ARM64_CODEGEN_H__
+
+#include <glib.h>
+
+enum {
+	ARMREG_R0 = 0,
+	ARMREG_R1 = 1,
+	ARMREG_R2 = 2,
+	ARMREG_R3 = 3,
+	ARMREG_R4 = 4,
+	ARMREG_R5 = 5,
+	ARMREG_R6 = 6,
+	ARMREG_R7 = 7,
+	ARMREG_R8 = 8,
+	ARMREG_R9 = 9,
+	ARMREG_R10 = 10,
+	ARMREG_R11 = 11,
+	ARMREG_R12 = 12,
+	ARMREG_R13 = 13,
+	ARMREG_R14 = 14,
+	ARMREG_R15 = 15,
+	ARMREG_R16 = 16,
+	ARMREG_R17 = 17,
+	ARMREG_R18 = 18,
+	ARMREG_R19 = 19,
+	ARMREG_R20 = 20,
+	ARMREG_R21 = 21,
+	ARMREG_R22 = 22,
+	ARMREG_R23 = 23,
+	ARMREG_R24 = 24,
+	ARMREG_R25 = 25,
+	ARMREG_R26 = 26,
+	ARMREG_R27 = 27,
+	ARMREG_R28 = 28,
+	ARMREG_R29 = 29,
+	ARMREG_R30 = 30,
+	ARMREG_SP = 31,
+	ARMREG_RZR = 31,
+
+	ARMREG_IP0 = ARMREG_R16,
+	ARMREG_IP1 = ARMREG_R17,
+	ARMREG_FP = ARMREG_R29,
+	ARMREG_LR = ARMREG_R30
+};
+
+enum {
+	ARMREG_D0 = 0,
+	ARMREG_D1 = 1,
+	ARMREG_D2 = 2,
+	ARMREG_D3 = 3,
+	ARMREG_D4 = 4,
+	ARMREG_D5 = 5,
+	ARMREG_D6 = 6,
+	ARMREG_D7 = 7,
+	ARMREG_D8 = 8,
+	ARMREG_D9 = 9,
+	ARMREG_D10 = 10,
+	ARMREG_D11 = 11,
+	ARMREG_D12 = 12,
+	ARMREG_D13 = 13,
+	ARMREG_D14 = 14,
+	ARMREG_D15 = 15,
+	ARMREG_D16 = 16,
+	ARMREG_D17 = 17,
+	ARMREG_D18 = 18,
+	ARMREG_D19 = 19,
+	ARMREG_D20 = 20,
+	ARMREG_D21 = 21,
+	ARMREG_D22 = 22,
+	ARMREG_D23 = 23,
+	ARMREG_D24 = 24,
+	ARMREG_D25 = 25,
+	ARMREG_D26 = 26,
+	ARMREG_D27 = 27,
+	ARMREG_D28 = 28,
+	ARMREG_D29 = 29,
+	ARMREG_D30 = 30,
+	ARMREG_D31 = 31
+};
+
+typedef enum {
+	ARMCOND_EQ = 0x0,          /* Equal; Z = 1 */
+	ARMCOND_NE = 0x1,          /* Not equal, or unordered; Z = 0 */
+	ARMCOND_CS = 0x2,          /* Carry set; C = 1 */
+	ARMCOND_HS = ARMCOND_CS,   /* Unsigned higher or same; */
+	ARMCOND_CC = 0x3,          /* Carry clear; C = 0 */
+	ARMCOND_LO = ARMCOND_CC,   /* Unsigned lower */
+	ARMCOND_MI = 0x4,          /* Negative; N = 1 */
+	ARMCOND_PL = 0x5,          /* Positive or zero; N = 0 */
+	ARMCOND_VS = 0x6,          /* Overflow; V = 1 */
+	ARMCOND_VC = 0x7,          /* No overflow; V = 0 */
+	ARMCOND_HI = 0x8,          /* Unsigned higher; C = 1 && Z = 0 */
+	ARMCOND_LS = 0x9,          /* Unsigned lower or same; C = 0 || Z = 1 */
+	ARMCOND_GE = 0xA,          /* Signed greater than or equal; N = V */
+	ARMCOND_LT = 0xB,          /* Signed less than; N != V */
+	ARMCOND_GT = 0xC,          /* Signed greater than; Z = 0 && N = V */
+	ARMCOND_LE = 0xD,          /* Signed less than or equal; Z = 1 || N != V */
+	ARMCOND_AL = 0xE,          /* Always */
+	ARMCOND_NV = 0xF,          /* Never */
+} ARMCond;
+
+typedef enum {
+	ARMSHIFT_LSL = 0x0,
+	ARMSHIFT_LSR = 0x1,
+	ARMSHIFT_ASR = 0x2
+} ARMShift;
+
+typedef enum {
+	ARMSIZE_B = 0x0,
+	ARMSIZE_H = 0x1,
+	ARMSIZE_W = 0x2,
+	ARMSIZE_X = 0x3
+} ARMSize;
+
+#define arm_emit(p, ins) do { *(guint32*)(p) = (ins); (p) += 4; } while (0)
+
+/* Overwrite bits [offset,offset+nbits] with value */
+static G_GNUC_UNUSED inline void
+arm_set_ins_bits (void *p, int offset, int nbits, guint32 value)
+{
+	*(guint32*)p = (*(guint32*)p & ~(((1 << nbits) - 1) << offset)) | (value << offset);
+}
+
+/*
+ * Naming conventions for codegen macros:
+ * - 64 bit opcodes have an 'X' suffix
+ * - 32 bit opcodes have a 'W' suffix
+ * - the order of operands is the same as in assembly
+ */
+
+/*
+ * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0487a/index.html
+ */
+
+/* Uncoditional branch (register) */
+
+// 0b1101011 == 0x6b
+#define arm_format_breg(p, opc, op2, op3, op4, rn) arm_emit ((p), (0x6b << 25) | ((opc) << 21) | ((op2) << 16) | ((op3) << 10) | ((rn) << 5) | ((op4) << 0))
+
+// 0b0000 == 0x0, 0b11111 == 0x1f
+#define arm_brx(p, reg) arm_format_breg ((p), 0x0, 0x1f, 0x0, 0x0, (reg))
+
+// 0b0001 == 0x1
+#define arm_blrx(p, reg) arm_format_breg ((p), 0x1, 0x1f, 0x0, 0x0, (reg))
+
+//0b0010 == 0x2
+#define arm_retx(p, reg) arm_format_breg ((p), 0x2, 0x1f, 0x0, 0x0, (reg))
+
+/* Unconditional branch (immeditate) */
+
+static G_GNUC_UNUSED inline gboolean
+arm_is_bl_disp (void *code, void *target)
+{
+	gint64 disp = ((char*)(target) - (char*)(code)) / 4;
+
+	return (disp > -(1 << 25)) && (disp < (1 << 25));
+}
+
+static G_GNUC_UNUSED inline unsigned int
+arm_get_disp (void *p, void *target)
+{
+	unsigned int disp = ((char*)target - (char*)p) / 4;
+
+	if (target)
+		g_assert (arm_is_bl_disp (p, target));
+
+	return (disp & 0x3ffffff);
+}
+
+// 0b00101 == 0x5
+#define arm_b(p, target) arm_emit (p, (0x0 << 31) | (0x5 << 26) | ((arm_get_disp ((p), (target)) << 0)))
+
+#define arm_bl(p, target) arm_emit (p, (0x1 << 31) | (0x5 << 26) | ((arm_get_disp ((p), (target)) << 0)))
+
+/* Conditional branch */
+
+static G_GNUC_UNUSED inline gboolean
+arm_is_disp19 (void *code, void *target)
+{
+	gint64 disp = ((char*)(target) - (char*)(code)) / 4;
+
+	return (disp > -(1 << 18)) && (disp < (1 << 18));
+}
+
+static G_GNUC_UNUSED inline unsigned int
+arm_get_disp19 (void *p, void *target)
+{
+	unsigned int disp = ((char*)target - (char*)p) / 4;
+
+	if (target)
+		g_assert (arm_is_disp19 (p, target));
+
+	return (disp & 0x7ffff);
+}
+
+// 0b0101010 == 0x2a
+#define arm_format_condbr(p, o1, o0, cond, disp) arm_emit ((p), (0x2a << 25) | ((o1) << 24) | ((disp) << 5) | ((o0) << 4) | ((cond) << 0))
+#define arm_get_bcc_cond(p) ((*(guint32*)p) & 0xf)
+
+#define arm_bcc(p, cond, target) arm_format_condbr ((p), 0x0, 0x0, (cond), arm_get_disp19 ((p), (target)))
+
+// 0b011010 == 0x1a
+#define arm_format_cmpbr(p, sf, op, rt, target) arm_emit ((p), ((sf) << 31) | (0x1a << 25) | ((op) << 24) | (arm_get_disp19 ((p), (target)) << 5) | ((rt) << 0))
+
+#define arm_set_cbz_target(p, target) arm_set_ins_bits (p, 5, 19, arm_get_disp19 ((p), (target)))
+
+#define arm_cbzx(p, rt, target) arm_format_cmpbr ((p), 0x1, 0x0, (rt), (target))
+#define arm_cbzw(p, rt, target) arm_format_cmpbr ((p), 0x0, 0x0, (rt), (target))
+
+#define arm_cbnzx(p, rt, target) arm_format_cmpbr ((p), 0x1, 0x1, (rt), (target))
+#define arm_cbnzw(p, rt, target) arm_format_cmpbr ((p), 0x0, 0x1, (rt), (target))
+
+static G_GNUC_UNUSED inline unsigned int
+arm_get_disp15 (void *p, void *target)
+{
+	unsigned int disp = ((char*)target - (char*)p) / 4;
+	return (disp & 0x7fff);
+}
+
+// 0b011011 == 0x1b
+#define arm_format_tbimm(p, op, rt, bit, target) arm_emit ((p), ((((bit) >> 5) & 1) << 31) | (0x1b << 25) | ((op) << 24) | (((bit) & 0x1f) << 19) | (arm_get_disp15 ((p), (target)) << 5) | ((rt) << 0))
+
+#define arm_tbz(p, rt, bit, target) arm_format_tbimm ((p), 0x0, (rt), (bit), (target))
+#define arm_tbnz(p, rt, bit, target) arm_format_tbimm ((p), 0x1, (rt), (bit), (target))
+
+/* Memory access */
+
+#define arm_is_pimm12_scaled(pimm,size) ((pimm) >= 0 && (pimm) / (size) <= 0xfff && ((pimm) % (size)) == 0)
+
+static G_GNUC_UNUSED unsigned int
+arm_encode_pimm12 (int pimm, int size)
+{
+	g_assert (arm_is_pimm12_scaled (pimm, size));
+	return ((unsigned int)(pimm / size)) & 0xfff;
+}
+
+#define arm_is_strb_imm(pimm) arm_is_pimm12_scaled((pimm), 1)
+#define arm_is_strh_imm(pimm) arm_is_pimm12_scaled((pimm), 2)
+#define arm_is_strw_imm(pimm) arm_is_pimm12_scaled((pimm), 4)
+#define arm_is_strx_imm(pimm) arm_is_pimm12_scaled((pimm), 8)
+
+/* Load/Store register + scaled immediate */
+/* No pre-index/post-index yet */
+#define arm_format_mem_imm(p, size, opc, rt, rn, pimm, scale) arm_emit ((p), ((size) << 30) | (0x39 << 24) | ((opc) << 22) | (arm_encode_pimm12 ((pimm), (scale)) << 10) | ((rn) << 5) | ((rt) << 0))
+
+/* C5.6.83 LDR (immediate) */
+#define arm_ldrx(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_X, 0x1, (rt), (rn), (pimm), 8)
+#define arm_ldrw(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_W, 0x1, (rt), (rn), (pimm), 4)
+/* C5.6.86 LDRB (immediate) */
+#define arm_ldrb(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_B, 0x1, (rt), (rn), (pimm), 1)
+/* C5.6.88 LDRH (immediate) */
+#define arm_ldrh(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_H, 0x1, (rt), (rn), (pimm), 2)
+/* C5.6.90 LDRSB (immediate) */
+#define arm_ldrsbx(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_B, 0x2, (rt), (rn), (pimm), 1)
+#define arm_ldrsbw(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_B, 0x3, (rt), (rn), (pimm), 1)
+/* C5.6.92 LDRSH (immediate) */
+#define arm_ldrshx(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_H, 0x2, (rt), (rn), (pimm), 2)
+#define arm_ldrshw(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_H, 0x3, (rt), (rn), (pimm), 2)
+/* C5.6.94 LDRSW (immediate) */
+#define arm_ldrswx(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_W, 0x2, (rt), (rn), (pimm), 4)
+
+/* C5.6.178 STR (immediate) */
+#define arm_strx(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_X, 0x0, (rt), (rn), (pimm), 8)
+#define arm_strw(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_W, 0x0, (rt), (rn), (pimm), 4)
+/* C5.6.182 STR (immediate) */
+#define arm_strh(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_H, 0x0, (rt), (rn), (pimm), 2)
+#define arm_strb(p, rt, rn, pimm) arm_format_mem_imm (p, ARMSIZE_B, 0x0, (rt), (rn), (pimm), 1)
+
+/* C3.3.9 Load/store register (immediate post-indexed) */
+static G_GNUC_UNUSED unsigned int
+arm_encode_simm9 (int simm)
+{
+	g_assert (simm >= -256 && simm <= 255);
+	return ((unsigned int)simm) & 0x1ff;
+}
+
+#define arm_format_mem_imm_post(p, size, V, opc, rt, rn, simm) arm_emit ((p), ((size) << 30) | (0x7 << 27) | ((V) << 26) | (0x0 << 24) | ((opc) << 22) | (arm_encode_simm9 ((simm)) << 12) | (0x1 << 10) | ((rn) << 5) | ((rt) << 0))
+
+#define arm_ldrx_post(p, rt, rn, simm) arm_format_mem_imm_post (p, ARMSIZE_X, 0x0, 0x1, (rt), (rn), (simm))
+#define arm_ldrw_post(p, rt, rn, simm) arm_format_mem_imm_post (p, ARMSIZE_W, 0x0, 0x1, (rt), (rn), (simm))
+
+#define arm_strx_post(p, rt, rn, simm) arm_format_mem_imm_post (p, ARMSIZE_X, 0x0, 0x0, (rt), (rn), (simm))
+#define arm_strw_post(p, rt, rn, simm) arm_format_mem_imm_post (p, ARMSIZE_W, 0x0, 0x0, (rt), (rn), (simm))
+
+/* C3.3.9 Load/store register (immediate pre-indexed) */
+#define arm_format_mem_imm_pre(p, size, V, opc, rt, rn, simm) arm_emit ((p), ((size) << 30) | (0x7 << 27) | ((V) << 26) | (0x0 << 24) | ((opc) << 22) | (arm_encode_simm9 ((simm)) << 12) | (0x3 << 10) | ((rn) << 5) | ((rt) << 0))
+
+#define arm_ldrx_pre(p, rt, rn, simm) arm_format_mem_imm_pre (p, ARMSIZE_X, 0x0, 0x1, (rt), (rn), (simm))
+#define arm_ldrw_pre(p, rt, rn, simm) arm_format_mem_imm_pre (p, ARMSIZE_W, 0x0, 0x1, (rt), (rn), (simm))
+
+#define arm_strx_pre(p, rt, rn, simm) arm_format_mem_imm_pre (p, ARMSIZE_X, 0x0, 0x0, (rt), (rn), (simm))
+#define arm_strw_pre(p, rt, rn, simm) arm_format_mem_imm_pre (p, ARMSIZE_W, 0x0, 0x0, (rt), (rn), (simm))
+
+/* Load/Store register + register */
+/* No extend/scale yet */
+#define arm_format_mem_reg(p, size, opc, rt, rn, rm) arm_emit ((p), ((size) << 30) | (0x38 << 24) | ((opc) << 22) | (0x1 << 21) | ((rm) << 16) | (0x3 << 13) | (0 << 12) | (0x2 << 10) | ((rn) << 5) | ((rt) << 0))
+
+/* C5.6.85 LDR (register) */
+#define arm_ldrx_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_X, 0x1, (rt), (rn), (rm))
+#define arm_ldrw_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_W, 0x1, (rt), (rn), (rm))
+/* C5.6.87 LDRB (register) */
+#define arm_ldrb_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_B, 0x1, (rt), (rn), (rm))
+/* C5.6.88 LDRH (register) */
+#define arm_ldrh_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_H, 0x1, (rt), (rn), (rm))
+/* C5.6.91 LDRSB (register) */
+#define arm_ldrsbx_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_B, 0x2, (rt), (rn), (rm))
+#define arm_ldrsbw_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_B, 0x3, (rt), (rn), (rm))
+/* C5.6.93 LDRSH (register) */
+#define arm_ldrshx_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_H, 0x2, (rt), (rn), (rm))
+#define arm_ldrshw_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_H, 0x3, (rt), (rn), (rm))
+/* C5.6.96 LDRSW (register) */
+#define arm_ldrswx_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_W, 0x2, (rt), (rn), (rm))
+
+/* C5.6.179 STR (register) */
+#define arm_strx_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_X, 0x0, (rt), (rn), (rm))
+#define arm_strw_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_W, 0x0, (rt), (rn), (rm))
+/* C5.6.181 STRB (register) */
+#define arm_strb_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_B, 0x0, (rt), (rn), (rm))
+/* C5.6.183 STRH (register) */
+#define arm_strh_reg(p, rt, rn, rm) arm_format_mem_reg ((p), ARMSIZE_H, 0x0, (rt), (rn), (rm))
+
+/* PC relative */
+
+/* C5.6.84 LDR (literal) */
+
+#define arm_get_ldr_lit_reg(p) (*(guint32*)(p) & 0x1f)
+
+#define arm_ldrx_lit(p, rt, target) arm_emit ((p), (0x01 << 30) | (0x18 << 24) | (arm_get_disp19 ((p), (target)) << 5) | ((rt) << 0))
+#define arm_ldrw_lit(p, rt, target) arm_emit ((p), (0x00 << 30) | (0x18 << 24) | (arm_get_disp19 ((p), (target)) << 5) | ((rt) << 0))
+#define arm_ldrswx_lit(p, rt, target) arm_emit ((p), (0x2 << 30) | (0x18 << 24) | (arm_get_disp19 ((p), (target)) << 5) | ((rt) << 0))
+
+/* Unscaled offset */
+/* FIXME: Not yet */
+
+/* Load/Store Pair */
+
+static G_GNUC_UNUSED unsigned int
+arm_encode_imm7 (int imm, int size)
+{
+	g_assert (imm / size >= -64 && imm / size <= 63 && (imm % size) == 0);
+	return ((unsigned int)(imm / size)) & 0x7f;
+}
+
+#define arm_is_imm7_scaled(imm, size) ((imm) / (size) >= -64 && (imm) / (size) <= 63 && ((imm) % (size)) == 0)
+
+#define arm_is_ldpx_imm(imm) arm_is_imm7_scaled ((imm), 8)
+
+/* C3.3.14 */
+#define arm_format_mem_p(p, size, opc, L, rt1, rt2, rn, imm) arm_emit ((p), (opc << 30) | (0x52 << 23) | ((L) << 22) | (arm_encode_imm7 (imm, size) << 15) | ((rt2) << 10) | ((rn) << 5) | ((rt1) << 0))
+
+#define arm_ldpx(p, rt1, rt2, rn, imm) arm_format_mem_p ((p), 8, 0x2, 1, (rt1), (rt2), (rn), (imm))
+#define arm_ldpw(p, rt1, rt2, rn, imm) arm_format_mem_p ((p), 4, 0x0, 1, (rt1), (rt2), (rn), (imm))
+#define arm_ldpsw(p, rt1, rt2, rn, imm) arm_format_mem_p ((p), 4, 0x1, 1, (rt1), (rt2), (rn), (imm))
+#define arm_stpx(p, rt1, rt2, rn, imm) arm_format_mem_p ((p), 8, 0x2, 0, (rt1), (rt2), (rn), (imm))
+#define arm_stpw(p, rt1, rt2, rn, imm) arm_format_mem_p ((p), 4, 0x0, 0, (rt1), (rt2), (rn), (imm))
+
+/* Load/Store Pair (Pre-indexed) */
+/* C3.3.16 */
+#define arm_format_mem_p_pre(p, size, opc, L, rt1, rt2, rn, imm) arm_emit ((p), (opc << 30) | (0x53 << 23) | ((L) << 22) | (arm_encode_imm7 (imm, size) << 15) | ((rt2) << 10) | ((rn) << 5) | ((rt1) << 0))
+
+#define arm_ldpx_pre(p, rt1, rt2, rn, imm) arm_format_mem_p_pre ((p), 8, 0x2, 1, (rt1), (rt2), (rn), (imm))
+#define arm_ldpw_pre(p, rt1, rt2, rn, imm) arm_format_mem_p_pre ((p), 4, 0x0, 1, (rt1), (rt2), (rn), (imm))
+#define arm_ldpsw_pre(p, rt1, rt2, rn, imm) arm_format_mem_p_pre ((p), 4, 0x1, 1, (rt1), (rt2), (rn), (imm))
+#define arm_stpx_pre(p, rt1, rt2, rn, imm) arm_format_mem_p_pre ((p), 8, 0x2, 0, (rt1), (rt2), (rn), (imm))
+#define arm_stpw_pre(p, rt1, rt2, rn, imm) arm_format_mem_p_pre ((p), 4, 0x0, 0, (rt1), (rt2), (rn), (imm))
+
+/* Not an official alias */
+#define arm_pushpx (p, rt1, rt2) arm_LDPX_pre (p, rt1, rt2, ARMREG_RSP, -8)
+
+/* Load/Store Pair (Post-indexed) */
+/* C3.3.15 */
+#define arm_format_mem_p_post(p, size, opc, L, rt1, rt2, rn, imm) arm_emit ((p), (opc << 30) | (0x51 << 23) | ((L) << 22) | (arm_encode_imm7 (imm, size) << 15) | ((rt2) << 10) | ((rn) << 5) | ((rt1) << 0))
+
+#define arm_ldpx_post(p, rt1, rt2, rn, imm) arm_format_mem_p_post ((p), 8, 0x2, 1, (rt1), (rt2), (rn), (imm))
+#define arm_ldpw_post(p, rt1, rt2, rn, imm) arm_format_mem_p_post ((p), 4, 0x0, 1, (rt1), (rt2), (rn), (imm))
+#define arm_ldpsw_post(p, rt1, rt2, rn, imm) arm_format_mem_p_post ((p), 4, 0x1, 1, (rt1), (rt2), (rn), (imm))
+#define arm_stpx_post(p, rt1, rt2, rn, imm) arm_format_mem_p_post ((p), 8, 0x2, 0, (rt1), (rt2), (rn), (imm))
+#define arm_stpw_post(p, rt1, rt2, rn, imm) arm_format_mem_p_post ((p), 4, 0x0, 0, (rt1), (rt2), (rn), (imm))
+
+/* Not an official alias */
+#define arm_poppx (p, rt1, rt2) arm_ldpx_post (p, rt1, rt2, ARMREG_RSP, 8)
+
+/* Load/Store Exclusive */
+#define arm_format_ldxr(p, size, rt, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x1 << 22) | (0x0 << 21) | (0x1f << 16) | (0x0 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0))
+#define arm_format_ldxp(p, size, rt1, rt2, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x1 << 22) | (0x1 << 21) | (0x1f << 16) | (0x0 << 15) | ((rt2) << 10)| ((rn) << 5) | ((rt1) << 0))
+#define arm_format_stxr(p, size, rs, rt, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x0 << 22) | (0x0 << 21) | ((rs) << 16) | (0x0 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0))
+#define arm_format_stxp(p, size, rs, rt1, rt2, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x0 << 22) | (0x1 << 21) | ((rs) << 16) | (0x0 << 15) | ((rt2) << 10)| ((rn) << 5) | ((rt1) << 0))
+
+#define arm_ldxrx(p, rt, rn) arm_format_ldxr ((p), ARMSIZE_X, (rt), (rn))
+#define arm_ldxrw(p, rt, rn) arm_format_ldxr ((p), ARMSIZE_W, (rt), (rn))
+#define arm_ldxrh(p, rt, rn) arm_format_ldxr ((p), ARMSIZE_H, (rt), (rn))
+#define arm_ldxrb(p, rt, rn) arm_format_ldxr ((p), ARMSIZE_B, (rt), (rn))
+#define arm_ldxpx(p, rt1, rt2, rn) arm_format_ldxp ((p), ARMSIZE_X, (rt1), (rt2), (rn))
+#define arm_ldxpw(p, rt1, rt2, rn) arm_format_ldxp ((p), ARMSIZE_W, (rt1), (rt2), (rn))
+#define arm_stxrx(p, rs, rt, rn) arm_format_stxr ((p), ARMSIZE_X, (rs), (rt), (rn))
+#define arm_stxrw(p, rs, rt, rn) arm_format_stxr ((p), ARMSIZE_W, (rs), (rt), (rn))
+#define arm_stxrh(p, rs, rt, rn) arm_format_stxr ((p), ARMSIZE_H, (rs), (rt), (rn))
+#define arm_stxrb(p, rs, rt, rn) arm_format_stxr ((p), ARMSIZE_B, (rs), (rt), (rn))
+#define arm_stxpx(p, rs, rt1, rt2, rn) arm_format_stxp ((p), ARMSIZE_X, (rs), (rt1), (rt2), (rn))
+#define arm_stxpw(p, rs, rt1, rt2, rn) arm_format_stxp ((p), ARMSIZE_W, (rs), (rt1), (rt2), (rn))
+
+/* C5.6.73 LDAR: Load-Acquire Register */
+
+#define arm_format_ldar(p, size, rt, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x1 << 23) | (0x1 << 22) | (0x0 << 21) | (0x1f << 16) | (0x1 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0))
+
+#define arm_ldarx(p, rt, rn) arm_format_ldar ((p), ARMSIZE_X, (rt), (rn))
+#define arm_ldarw(p, rt, rn) arm_format_ldar ((p), ARMSIZE_W, (rt), (rn))
+#define arm_ldarh(p, rt, rn) arm_format_ldar ((p), ARMSIZE_H, (rt), (rn))
+#define arm_ldarb(p, rt, rn) arm_format_ldar ((p), ARMSIZE_B, (rt), (rn))
+
+/* C5.6.169 STLR: Store-Release Register */
+
+#define arm_format_stlr(p, size, rt, rn) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x1 << 23) | (0x0 << 22) | (0x0 << 21) | (0x1f << 16) | (0x1 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0))
+
+#define arm_stlrx(p, rn, rt) arm_format_stlr ((p), ARMSIZE_X, (rt), (rn))
+#define arm_stlrw(p, rn, rt) arm_format_stlr ((p), ARMSIZE_W, (rt), (rn))
+#define arm_stlrh(p, rn, rt) arm_format_stlr ((p), ARMSIZE_H, (rt), (rn))
+#define arm_stlrb(p, rn, rt) arm_format_stlr ((p), ARMSIZE_B, (rt), (rn))
+
+/* C5.6.77 LDAXR */
+#define arm_format_ldaxr(p, size, rn, rt) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x1 << 22) | (0x0 << 21) | (0x1f << 16) | (0x1 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0))
+
+#define arm_ldaxrx(p, rt, rn) arm_format_ldaxr ((p), 0x3, (rn), (rt))
+#define arm_ldaxrw(p, rt, rn) arm_format_ldaxr ((p), 0x2, (rn), (rt))
+
+/* C5.6.173 STLXR */
+#define arm_format_stlxr(p, size, rs, rn, rt) arm_emit ((p), ((size) << 30) | (0x8 << 24) | (0x0 << 23) | (0x0 << 22) | (0x0 << 21) | ((rs) << 16) | (0x1 << 15) | (0x1f << 10) | ((rn) << 5) | ((rt) << 0))
+
+#define arm_stlxrx(p, rs, rt, rn) arm_format_stlxr ((p), 0x3, (rs), (rn), (rt))
+#define arm_stlxrw(p, rs, rt, rn) arm_format_stlxr ((p), 0x2, (rs), (rn), (rt))
+
+/* Load/Store SIMD&FP */
+
+/* C6.3.285 STR (immediate, SIMD&FP) */
+#define arm_format_strfp_imm(p, size, opc, rt, rn, pimm, scale) arm_emit ((p), ((size) << 30) | (0xf << 26) | (0x1 << 24) | ((opc) << 22) | (arm_encode_pimm12 ((pimm), (scale)) << 10) | ((rn) << 5) | ((rt) << 0))
+
+/* Store double */
+#define arm_strfpx(p, dt, xn, simm) arm_format_strfp_imm ((p), ARMSIZE_X, 0x0, (dt), (xn), (simm), 8)
+/* Store single */
+#define arm_strfpw(p, st, xn, simm) arm_format_strfp_imm ((p), ARMSIZE_W, 0x0, (st), (xn), (simm), 4)
+
+/* C6.3.166 LDR (immediate, SIMD&FP) */
+#define arm_format_ldrfp_imm(p, size, opc, rt, rn, pimm, scale) arm_emit ((p), ((size) << 30) | (0xf << 26) | (0x1 << 24) | ((opc) << 22) | (arm_encode_pimm12 ((pimm), (scale)) << 10) | ((rn) << 5) | ((rt) << 0))
+
+/* Load double */
+#define arm_ldrfpx(p, dt, xn, simm) arm_format_ldrfp_imm ((p), ARMSIZE_X, 0x1, dt, xn, simm, 8)
+/* Load single */
+#define arm_ldrfpw(p, dt, xn, simm) arm_format_ldrfp_imm ((p), ARMSIZE_W, 0x1, dt, xn, simm, 4)
+
+/* Arithmetic (immediate) */
+static G_GNUC_UNUSED inline guint32
+arm_encode_arith_imm (int imm, guint32 *shift)
+{
+	// FIXME:
+	g_assert ((imm >= 0) && (imm < 0xfff));
+	*shift = 0;
+	return (guint32)imm;
+}
+
+// FIXME:
+#define arm_is_arith_imm(imm)  (((imm) >= 0) && ((imm) < 0xfff))
+
+#define arm_format_alu_imm(p, sf, op, S, rd, rn, imm) do { \
+	guint32 _imm12, _shift; \
+	_imm12 = arm_encode_arith_imm ((imm), &_shift); arm_emit ((p), ((sf) << 31) | ((op) << 30) | ((S) << 29) | (0x11 << 24) | ((_shift) << 22) | ((_imm12) << 10) | ((rn) << 5) | ((rd) << 0)); \
+} while (0)
+
+/* rd/rn can be SP for addx/subx */
+#define arm_addx_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x1, 0x0, 0x0, (rd), (rn), (imm))
+#define arm_addw_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x0, 0x0, 0x0, (rd), (rn), (imm))
+#define arm_addsx_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x1, 0x0, 0x1, (rd), (rn), (imm))
+#define arm_addsw_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x0, 0x0, 0x1, (rd), (rn), (imm))
+#define arm_subx_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x1, 0x1, 0x0, (rd), (rn), (imm))
+#define arm_subw_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x0, 0x1, 0x0, (rd), (rn), (imm))
+#define arm_subsx_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x1, 0x1, 0x1, (rd), (rn), (imm))
+#define arm_subsw_imm(p, rd, rn, imm) arm_format_alu_imm ((p), 0x0, 0x1, 0x1, (rd), (rn), (imm))
+
+#define arm_cmpx_imm(p, rn, imm) arm_subsx_imm ((p), ARMREG_RZR, (rn), (imm))
+#define arm_cmpw_imm(p, rn, imm) arm_subsw_imm ((p), ARMREG_RZR, (rn), (imm))
+#define arm_cmnx_imm(p, rn, imm) arm_addsx_imm ((p), ARMREG_RZR, (rn), (imm))
+#define arm_cmnw_imm(p, rn, imm) arm_addsw_imm ((p), ARMREG_RZR, (rn), (imm))
+
+/* Logical (immediate) */
+
+// FIXME: imm
+#if 0
+#define arm_format_and(p, sf, opc, rd, rn, imm) arm_emit ((p), ((sf) << 31) | ((opc) << 29) | (0x24 << 23) | ((0) << 22) | ((imm) << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_andx_imm(p, rd, rn, imm) arm_format_and ((p), 0x1, 0x0, (rd), (rn), (imm))
+#define arm_andw_imm(p, rd, rn, imm) arm_format_and ((p), 0x0, 0x0, (rd), (rn), (imm))
+#define arm_andsx_imm(p, rd, rn, imm) arm_format_and ((p), 0x1, 0x3, (rd), (rn), (imm))
+#define arm_andsw_imm(p, rd, rn, imm) arm_format_and ((p), 0x0, 0x3, (rd), (rn), (imm))
+#define arm_eorx_imm(p, rd, rn, imm) arm_format_and ((p), 0x1, 0x2, (rd), (rn), (imm))
+#define arm_eorw_imm(p, rd, rn, imm) arm_format_and ((p), 0x0, 0x2, (rd), (rn), (imm))
+#define arm_orrx_imm(p, rd, rn, imm) arm_format_and ((p), 0x1, 0x1, (rd), (rn), (imm))
+#define arm_orrw_imm(p, rd, rn, imm) arm_format_and ((p), 0x0, 0x1, (rd), (rn), (imm))
+
+#define arm_tstx_imm(p, rn, imm) arm_andsx_imm ((p), ARMREG_RZR, (rn), (imm))
+#define arm_tstw_imm(p, rn, imm) arm_andsw_imm ((p), ARMREG_RZR, (rn), (imm))
+#endif
+
+/* Move (wide immediate) */
+#define arm_format_mov(p, sf, opc, hw, rd, imm16) arm_emit ((p), ((sf) << 31) | ((opc) << 29) | (0x25 << 23) | ((hw) << 21) | (((guint32)(imm16) & 0xffff) << 5) | ((rd) << 0))
+
+#define arm_get_movzx_rd(p) ((*(guint32*)p) & 0x1f)
+
+#define arm_movzx(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x1, 0x2, (shift) / 16, (rd), (imm)); } while (0)
+#define arm_movzw(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x0, 0x2, (shift) / 16, (rd), (imm)); } while (0)
+#define arm_movnx(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x1, 0x0, (shift) / 16, (rd), (imm)); } while (0)
+#define arm_movnw(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x0, 0x0, (shift) / 16, (rd), (imm)); } while (0)
+#define arm_movkx(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x1, 0x3, (shift) / 16, (rd), (imm)); } while (0)
+#define arm_movkw(p, rd, imm, shift) do { g_assert ((shift) % 16 == 0); arm_format_mov ((p), 0x0, 0x3, (shift) / 16, (rd), (imm)); } while (0)
+
+/* PC-relative address calculation */
+#define arm_format_adrp(p, op, rd, target) do { guint64 imm1 = (guint64)(target); guint64 imm2 = (guint64)(p); int _imm = imm1 - imm2; arm_emit ((p), ((op) << 31) | (((_imm) & 0x3) << 29) | (0x10 << 24) | (((_imm >> 2) & 0x7ffff) << 5) | ((rd) << 0)); } while (0)
+
+#define arm_adrpx(p, rd, target) arm_format_adrp ((p), 0x1, (rd), (target))
+#define arm_adrx(p, rd, target) arm_format_adrp ((p), 0x0, (rd), (target))
+
+/* Bitfield move */
+#define arm_format_bfm(p, sf, opc, N, immr, imms, rn, rd) arm_emit ((p), ((sf) << 31) | ((opc) << 29) | (0x26 << 23) | ((N) << 22) | ((N) << 22) | ((immr) << 16) | ((imms) << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_bfmx(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x1, 0x1, 0x1, (immr), (imms), (rn), (rd))
+#define arm_bfmw(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x0, 0x1, 0x0, (immr), (imms), (rn), (rd))
+#define arm_sbfmx(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x1, 0x0, 0x1, (immr), (imms), (rn), (rd))
+#define arm_sbfmw(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x0, 0x0, 0x0, (immr), (imms), (rn), (rd))
+#define arm_ubfmx(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x1, 0x2, 0x1, (immr), (imms), (rn), (rd))
+#define arm_ubfmw(p, rd, rn, immr, imms) arm_format_bfm ((p), 0x0, 0x2, 0x0, (immr), (imms), (rn), (rd))
+
+/* Sign extend and Zero-extend */
+#define arm_sxtbx(p, rd, rn) arm_sbfmx ((p), (rd), (rn), 0, 7)
+#define arm_sxtbw(p, rd, rn) arm_sbfmw ((p), (rd), (rn), 0, 7)
+#define arm_sxthx(p, rd, rn) arm_sbfmx ((p), (rd), (rn), 0, 15)
+#define arm_sxthw(p, rd, rn) arm_sbfmw ((p), (rd), (rn), 0, 15)
+#define arm_sxtwx(p, rd, rn) arm_sbfmx ((p), (rd), (rn), 0, 31)
+#define arm_uxtbx(p, rd, rn) arm_ubfmx ((p), (rd), (rn), 0, 7)
+#define arm_uxtbw(p, rd, rn) arm_ubfmw ((p), (rd), (rn), 0, 7)
+#define arm_uxthx(p, rd, rn) arm_ubfmx ((p), (rd), (rn), 0, 15)
+#define arm_uxthw(p, rd, rn) arm_ubfmw ((p), (rd), (rn), 0, 15)
+
+/* Extract register */
+#define arm_format_extr(p, sf, N, rd, rn, rm, imms) arm_emit ((p), ((sf) << 31) | (0x27 << 23) | ((N) << 22) | (0x0 << 21) | ((rm) << 16) | ((imms) << 10) | ((rn) << 5) | ((rd) << 0))
+#define arm_extrx(p, rd, rn, rm, lsb) arm_format_extr ((p), 0x1, 0x1, (rd), (rn), (rm), (lsb))
+#define arm_extrw(p, rd, rn, rm, lsb) arm_format_extr ((p), 0x0, 0x0, (rd), (rn), (rm), (lsb))
+
+/* Shift (immediate) */
+#define arm_asrx(p, rd, rn, shift) arm_sbfmx ((p), (rd), (rn), (shift), 63)
+#define arm_asrw(p, rd, rn, shift) arm_sbfmw ((p), (rd), (rn), (shift), 31)
+#define arm_lslx(p, rd, rn, shift) arm_ubfmx ((p), (rd), (rn), 64 - ((shift) % 64), 63 - ((shift) % 64))
+#define arm_lslw(p, rd, rn, shift) arm_ubfmw ((p), (rd), (rn), 32 - ((shift) % 32), 31 - ((shift) % 32))
+#define arm_lsrx(p, rd, rn, shift) arm_ubfmx ((p), (rd), (rn), shift, 63)
+#define arm_lsrw(p, rd, rn, shift) arm_ubfmw ((p), (rd), (rn), shift, 31)
+#define arm_rorx(p, rd, rs, shift) arm_extrx ((p), (rd), (rs), (rs), (shift))
+#define arm_rorw(p, rd, rs, shift) arm_extrw ((p), (rd), (rs), (rs), (shift))
+
+/* Arithmetic (shifted register) */
+#define arm_format_alu_shift(p, sf, op, S, rd, rn, rm, shift, imm6) arm_emit ((p), ((sf) << 31) | ((op) << 30) | ((S) << 29) | (0xb << 24) | ((shift) << 22) | (0x0 << 21) | ((rm) << 16) | ((imm6) << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_addx_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x1, 0x0, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_addw_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x0, 0x0, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_addsx_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x1, 0x0, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_addsw_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x0, 0x0, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_subx_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x1, 0x1, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_subw_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x0, 0x1, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_subsx_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x1, 0x1, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_subsw_shift(p, rd, rn, rm, shift_type, amount) arm_format_alu_shift ((p), 0x0, 0x1, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_cmnx_shift(p, rn, rm, shift_type, amount) arm_addsx_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount))
+#define arm_cmnw_shift(p, rn, rm, shift_type, amount) arm_addsw_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount))
+#define arm_cmpx_shift(p, rn, rm, shift_type, amount) arm_subsx_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount))
+#define arm_cmpw_shift(p, rn, rm, shift_type, amount) arm_subsw_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount))
+#define arm_negx_shift(p, rd, rm, shift_type, amount) arm_subx_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount))
+#define arm_negw_shift(p, rd, rm, shift_type, amount) arm_subw_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount))
+#define arm_negsx_shift(p, rd, rm, shift_type, amount) arm_subsx_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount))
+#define arm_negsw_shift(p, rd, rm, shift_type, amount) arm_subsw_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount))
+
+#define arm_addx(p, rd, rn, rm) arm_addx_shift ((p), (rd), (rn), (rm), 0, 0)
+#define arm_addw(p, rd, rn, rm) arm_addw_shift ((p), (rd), (rn), (rm), 0, 0)
+#define arm_subx(p, rd, rn, rm) arm_subx_shift ((p), (rd), (rn), (rm), 0, 0)
+#define arm_subw(p, rd, rn, rm) arm_subw_shift ((p), (rd), (rn), (rm), 0, 0)
+#define arm_addsx(p, rd, rn, rm) arm_addsx_shift ((p), (rd), (rn), (rm), 0, 0)
+#define arm_addsw(p, rd, rn, rm) arm_addsw_shift ((p), (rd), (rn), (rm), 0, 0)
+#define arm_subsx(p, rd, rn, rm) arm_subsx_shift ((p), (rd), (rn), (rm), 0, 0)
+#define arm_subsw(p, rd, rn, rm) arm_subsw_shift ((p), (rd), (rn), (rm), 0, 0)
+#define arm_cmpx(p, rd, rn) arm_cmpx_shift ((p), (rd), (rn), 0, 0)
+#define arm_cmpw(p, rd, rn) arm_cmpw_shift ((p), (rd), (rn), 0, 0)
+#define arm_negx(p, rd, rn) arm_negx_shift ((p), (rd), (rn), 0, 0)
+#define arm_negw(p, rd, rn) arm_negw_shift ((p), (rd), (rn), 0, 0)
+
+/* Arithmetic with carry */
+#define arm_format_adc(p, sf, op, S, rd, rn, rm) arm_emit ((p), ((sf) << 31) | ((op) << 30) | ((S) << 29) | (0xd0 << 21) | ((rm) << 16) | (0x0 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_adcx(p, rd, rn, rm) arm_format_adc ((p), 0x1, 0x0, 0x0, (rd), (rn), (rm))
+#define arm_adcw(p, rd, rn, rm) arm_format_adc ((p), 0x0, 0x0, 0x0, (rd), (rn), (rm))
+#define arm_adcsx(p, rd, rn, rm) arm_format_adc ((p), 0x1, 0x0, 0x1, (rd), (rn), (rm))
+#define arm_adcsw(p, rd, rn, rm) arm_format_adc ((p), 0x0, 0x0, 0x1, (rd), (rn), (rm))
+#define arm_sbcx(p, rd, rn, rm) arm_format_adc ((p), 0x1, 0x1, 0x0, (rd), (rn), (rm))
+#define arm_sbcw(p, rd, rn, rm) arm_format_adc ((p), 0x0, 0x1, 0x0, (rd), (rn), (rm))
+#define arm_sbcsx(p, rd, rn, rm) arm_format_adc ((p), 0x1, 0x1, 0x1, (rd), (rn), (rm))
+#define arm_sbcsw(p, rd, rn, rm) arm_format_adc ((p), 0x0, 0x1, 0x1, (rd), (rn), (rm))
+#define arm_ngcx(p, rd, rm) arm_sbcx ((p), (rd), ARMREG_RZR, (rm))
+#define arm_ngcw(p, rd, rm) arm_sbcw ((p), (rd), ARMREG_RZR, (rm))
+#define arm_ngcsx(p, rd, rm) arm_sbcsx ((p), (rd), ARMREG_RZR, (rm))
+#define arm_ngcsw(p, rd, rm) arm_sbcsw ((p), (rd), ARMREG_RZR, (rm))
+
+/* Logical (shifted register) */
+#define arm_format_logical_shift(p, sf, op, N, rd, rn, rm, shift, imm6) arm_emit ((p), ((sf) << 31) | ((op) << 29) | (0xa << 24) | ((shift) << 22) | ((N) << 21) | ((rm) << 16) | ((imm6) << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_andx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x0, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_andw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x0, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_andsx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x3, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_andsw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x3, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_bicx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x0, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_bicw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x0, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_bicsx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x3, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_bicsw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x3, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_eonx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x2, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_eonw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x2, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_eorx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x2, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_eorw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x2, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_orrx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x1, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_orrw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x1, 0x0, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_ornx_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x1, 0x1, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_ornw_shift(p, rd, rn, rm, shift_type, amount) arm_format_logical_shift ((p), 0x0, 0x1, 0x1, (rd), (rn), (rm), (shift_type), (amount))
+#define arm_mvnx_shift(p, rd, rm, shift_type, amount) arm_ornx_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount))
+#define arm_mvnw_shift(p, rd, rm, shift_type, amount) arm_ornw_shift ((p), (rd), ARMREG_RZR, (rm), (shift_type), (amount))
+#define arm_tstx_shift(p, rn, rm, shift_type, amount) arm_andsx_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount))
+#define arm_tstw_shift(p, rn, rm, shift_type, amount) arm_andsw_shift ((p), ARMREG_RZR, (rn), (rm), (shift_type), (amount))
+/* Aliases */
+#define arm_andx(p, rd, rn, rm) arm_andx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_andw(p, rd, rn, rm) arm_andw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_andsx(p, rd, rn, rm) arm_andsx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_andsw(p, rd, rn, rm) arm_andsw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_bixx(p, rd, rn, rm) arm_bixx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_bixw(p, rd, rn, rm) arm_bixw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_bixsx(p, rd, rn, rm) arm_bixsx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_bixsw(p, rd, rn, rm) arm_bixsw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_eonx(p, rd, rn, rm) arm_eonx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_eonw(p, rd, rn, rm) arm_eonw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_eorx(p, rd, rn, rm) arm_eorx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_eorw(p, rd, rn, rm) arm_eorw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_orrx(p, rd, rn, rm) arm_orrx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_orrw(p, rd, rn, rm) arm_orrw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_ornx(p, rd, rn, rm) arm_ornx_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_ornw(p, rd, rn, rm) arm_ornw_shift(p, rd, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_mvnx(p, rd, rm) arm_mvnx_shift(p, rd, rm, ARMSHIFT_LSL, 0)
+#define arm_mvnw(p, rd, rm) arm_mvnw_shift(p, rd, rm, ARMSHIFT_LSL, 0)
+#define arm_tstx(p, rn, rm) arm_tstx_shift(p, rn, rm, ARMSHIFT_LSL, 0)
+#define arm_tstw(p, rn, rm) arm_tstw_shift(p, rn, rm, ARMSHIFT_LSL, 0)
+
+/* Move (register) */
+#define arm_movx(p, rn, rm) arm_orrx_shift ((p), (rn), ARMREG_RZR, (rm), ARMSHIFT_LSL, 0)
+#define arm_movw(p, rn, rm) arm_orrw_shift ((p), (rn), ARMREG_RZR, (rm), ARMSHIFT_LSL, 0)
+
+/* Not an official alias */
+#define arm_movspx(p, rn, rm) arm_addx_imm ((p), (rn), (rm), 0)
+
+/* Shift (register) */
+#define arm_format_shift_reg(p, sf, op2, rd, rn, rm) arm_emit ((p), ((sf) << 31) | (0xd6 << 21) | ((rm) << 16) | (0x2 << 12) | ((op2) << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_asrvx(p, rd, rn, rm) arm_format_shift_reg ((p), 0x1, 0x2, (rd), (rn), (rm))
+#define arm_asrvw(p, rd, rn, rm) arm_format_shift_reg ((p), 0x0, 0x2, (rd), (rn), (rm))
+#define arm_lslvx(p, rd, rn, rm) arm_format_shift_reg ((p), 0x1, 0x0, (rd), (rn), (rm))
+#define arm_lslvw(p, rd, rn, rm) arm_format_shift_reg ((p), 0x0, 0x0, (rd), (rn), (rm))
+#define arm_lsrvx(p, rd, rn, rm) arm_format_shift_reg ((p), 0x1, 0x1, (rd), (rn), (rm))
+#define arm_lsrvw(p, rd, rn, rm) arm_format_shift_reg ((p), 0x0, 0x1, (rd), (rn), (rm))
+#define arm_rorvx(p, rd, rn, rm) arm_format_shift_reg ((p), 0x1, 0x3, (rd), (rn), (rm))
+#define arm_rorvw(p, rd, rn, rm) arm_format_shift_reg ((p), 0x0, 0x3, (rd), (rn), (rm))
+
+/* Multiply */
+#define arm_format_mul(p, sf, o0, rd, rn, rm, ra) arm_emit ((p), ((sf) << 31) | (0x0 << 29) | (0x1b << 24) | (0x0 << 21) | ((rm) << 16) | ((o0) << 15) | ((ra) << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_maddx(p, rd, rn, rm, ra) arm_format_mul((p), 0x1, 0x0, (rd), (rn), (rm), (ra))
+#define arm_maddw(p, rd, rn, rm, ra) arm_format_mul((p), 0x0, 0x0, (rd), (rn), (rm), (ra))
+#define arm_msubx(p, rd, rn, rm, ra) arm_format_mul((p), 0x1, 0x1, (rd), (rn), (rm), (ra))
+#define arm_msubw(p, rd, rn, rm, ra) arm_format_mul((p), 0x0, 0x1, (rd), (rn), (rm), (ra))
+#define arm_mnegx(p, rd, rn, rm) arm_msubx ((p), (rd), (rn), (rm), ARMREG_RZR)
+#define arm_mnegw(p, rd, rn, rm) arm_msubw ((p), (rd), (rn), (rm), ARMREG_RZR)
+#define arm_mulx(p, rd, rn, rm) arm_maddx ((p), (rd), (rn), (rm), ARMREG_RZR)
+#define arm_mulw(p, rd, rn, rm) arm_maddw ((p), (rd), (rn), (rm), ARMREG_RZR)
+
+/* FIXME: Missing multiple opcodes */
+
+/* Division */
+#define arm_format_div(p, sf, o1, rd, rn, rm) arm_emit ((p), ((sf) << 31) | (0xd6 << 21) | ((rm) << 16) | (0x1 << 11) | ((o1) << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_sdivx(p, rd, rn, rm) arm_format_div ((p), 0x1, 0x1, (rd), (rn), (rm))
+#define arm_sdivw(p, rd, rn, rm) arm_format_div ((p), 0x0, 0x1, (rd), (rn), (rm))
+#define arm_udivx(p, rd, rn, rm) arm_format_div ((p), 0x1, 0x0, (rd), (rn), (rm))
+#define arm_udivw(p, rd, rn, rm) arm_format_div ((p), 0x0, 0x0, (rd), (rn), (rm))
+
+/* Conditional select */
+#define arm_format_csel(p, sf, op, op2, cond, rd, rn, rm) arm_emit ((p), ((sf) << 31) | ((op) << 30) | (0xd4 << 21) | ((rm) << 16) | ((cond) << 12) | ((op2) << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_cselx(p, cond, rd, rn, rm) arm_format_csel ((p), 0x1, 0x0, 0x0, (cond), (rd), (rn), (rm))
+#define arm_cselw(p, cond, rd, rn, rm) arm_format_csel ((p), 0x0, 0x0, 0x0, (cond), (rd), (rn), (rm))
+#define arm_csincx(p, cond, rd, rn, rm) arm_format_csel ((p), 0x1, 0x0, 0x1, (cond), (rd), (rn), (rm))
+#define arm_csincw(p, cond, rd, rn, rm) arm_format_csel ((p), 0x0, 0x0, 0x1, (cond), (rd), (rn), (rm))
+#define arm_csinvx(p, cond, rd, rn, rm) arm_format_csel ((p), 0x1, 0x1, 0x0, (cond), (rd), (rn), (rm))
+#define arm_csinvw(p, cond, rd, rn, rm) arm_format_csel ((p), 0x0, 0x1, 0x0, (cond), (rd), (rn), (rm))
+#define arm_csnegx(p, cond, rd, rn, rm) arm_format_csel ((p), 0x1, 0x1, 0x1, (cond), (rd), (rn), (rm))
+#define arm_csnegw(p, cond, rd, rn, rm) arm_format_csel ((p), 0x0, 0x1, 0x1, (cond), (rd), (rn), (rm))
+
+#define arm_cset(p, cond, rd) arm_csincx ((p), ((cond) ^ 0x1), (rd), ARMREG_RZR, ARMREG_RZR)
+
+/* C5.6.68 (HINT) */
+#define arm_hint(p, imm) arm_emit ((p), (0xd5032 << 12) | ((imm) << 5) | (0x1f << 0))
+#define arm_nop(p) arm_hint ((p), 0x0)
+
+/* C5.6.29 BRK */
+#define arm_brk(p, imm) arm_emit ((p), (0xd4 << 24) | (0x1 << 21) | ((imm) << 5))
+
+/* C6.3.114 FMOV (General) */
+#define arm_format_fmov_gr(p, sf, type, rmode, opcode, rn, rd) arm_emit ((p), ((sf) << 31) | (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rmode) << 19) | ((opcode) << 16) | ((rn) << 5) | ((rd) << 0))
+
+/* Move gr->vfp */
+#define arm_fmov_rx_to_double(p, dd, xn) arm_format_fmov_gr ((p), 0x1, 0x1, 0x0, 0x7, (xn), (dd))
+
+/* Move vfp->gr */
+#define arm_fmov_double_to_rx(p, xd, dn) arm_format_fmov_gr ((p), 0x1, 0x1, 0x0, 0x6, (dn), (xd))
+
+/* C6.3.113 FMOV (register) */
+#define arm_format_fmov(p, type, rn, rd) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | (0x10 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_fmovd(p, dd, dn) arm_format_fmov ((p), 0x1, (dn), (dd))
+#define arm_fmovs(p, dd, dn) arm_format_fmov ((p), 0x0, (dn), (dd))
+
+/* C6.3.54 FCMP */
+#define arm_format_fcmp(p, type, opc, rn, rm) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rm) << 16) | (0x8 << 10) | ((rn) << 5) | ((opc) << 3))
+
+#define arm_fcmpd(p, dn, dm) arm_format_fcmp (p, 0x1, 0x0, (dn), (dm))
+#define arm_fcmps(p, dn, dm) arm_format_fcmp (p, 0x0, 0x0, (dn), (dm))
+
+/* Float precision */
+#define arm_format_fcvt(p, type, opc, rn, rd) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | (0x1 << 17) | ((opc) << 15) | (0x10 << 10) | ((rn) << 5) | ((rd) << 0))
+
+/* C6.3.57 FCVT */
+/* single->double */
+#define arm_fcvt_sd(p, dd, sn) arm_format_fcvt ((p), 0x0, 0x1, (sn), (dd))
+/* double->single */
+#define arm_fcvt_ds(p, sd, dn) arm_format_fcvt ((p), 0x1, 0x0, (dn), (sd))
+
+/* Float conversion to integer conversion */
+#define arm_format_fcvtz(p, sf, type, rmode, opcode, rn, rd) arm_emit ((p), ((sf) << 31) | (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rmode) << 19) | ((opcode) << 16) | ((rn) << 5) | ((rd) << 0))
+
+/* C6.3.80 FCVTZS (scalar, integer) */
+#define arm_fcvtzs_dw(p, rd, rn) arm_format_fcvtz ((p), 0x0, 0x1, 0x3, 0x0, (rn), (rd))
+#define arm_fcvtzs_dx(p, rd, rn) arm_format_fcvtz ((p), 0x1, 0x1, 0x3, 0x0, (rn), (rd))
+#define arm_fcvtzs_sw(p, rd, rn) arm_format_fcvtz ((p), 0x0, 0x0, 0x3, 0x0, (rn), (rd))
+#define arm_fcvtzs_sx(p, rd, rn) arm_format_fcvtz ((p), 0x1, 0x0, 0x3, 0x0, (rn), (rd))
+
+/* C6.3.84 FCVTZU (scalar, integer) */
+#define arm_fcvtzu_dw(p, rd, rn) arm_format_fcvtz ((p), 0x0, 0x1, 0x3, 0x1, (rn), (rd))
+#define arm_fcvtzu_dx(p, rd, rn) arm_format_fcvtz ((p), 0x1, 0x1, 0x3, 0x1, (rn), (rd))
+#define arm_fcvtzu_sw(p, rd, rn) arm_format_fcvtz ((p), 0x0, 0x0, 0x3, 0x1, (rn), (rd))
+#define arm_fcvtzu_sx(p, rd, rn) arm_format_fcvtz ((p), 0x1, 0x0, 0x3, 0x1, (rn), (rd))
+
+/* C6.3.208 SCVTF (vector, integer) */
+#define arm_format_scvtf_vector(p, sz, rn, rd) arm_emit ((p), (0x1 << 30) | (0x0 << 29) | (0x1e << 24) | ((sz) << 22) | (0x10 << 17) | (0x1d << 12) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_scvtf_d(p, dd, dn) arm_format_scvtf_vector ((p), 0x1, (dn), (dd))
+#define arm_scvtf_s(p, sd, sn) arm_format_scvtf_vector ((p), 0x0, (sn), (sd))
+
+/* C6.3.210 SCVTF (scalar, integer) */
+#define arm_format_scvtf_scalar(p, sf, type, rn, rd) arm_emit ((p), ((sf) << 31) | (0x1e << 24) | ((type) << 22) | (0x1 << 21) | (0x2 << 16) | (0x0 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_scvtf_rx_to_d(p, dd, rn) arm_format_scvtf_scalar ((p), 0x1, 0x1, rn, dd)
+#define arm_scvtf_rw_to_d(p, dd, rn) arm_format_scvtf_scalar ((p), 0x0, 0x1, rn, dd)
+#define arm_scvtf_rx_to_s(p, dd, rn) arm_format_scvtf_scalar ((p), 0x1, 0x0, rn, dd)
+#define arm_scvtf_rw_to_s(p, dd, rn) arm_format_scvtf_scalar ((p), 0x0, 0x0, rn, dd)
+
+/* C6.3.306 UCVTF (vector, integer) */
+#define arm_format_ucvtf_vector(p, sz, rn, rd) arm_emit ((p), (0x1 << 30) | (0x1 << 29) | (0x1e << 24) | ((sz) << 22) | (0x10 << 17) | (0x1d << 12) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_ucvtf_d(p, dd, dn) arm_format_ucvtf_vector ((p), 0x1, (dn), (dd))
+#define arm_ucvtf_s(p, sd, sn) arm_format_ucvtf_vector ((p), 0x0, (sn), (sd))
+
+/* C6.3.308 UCVTF (scalar, integer) */
+#define arm_format_ucvtf_scalar(p, sf, type, rn, rd) arm_emit ((p), ((sf) << 31) | (0x1e << 24) | ((type) << 22) | (0x1 << 21) | (0x3 << 16) | (0x0 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_ucvtf_rx_to_d(p, dd, rn) arm_format_ucvtf_scalar ((p), 0x1, 0x1, rn, dd)
+#define arm_ucvtf_rw_to_d(p, dd, rn) arm_format_ucvtf_scalar ((p), 0x0, 0x1, rn, dd)
+
+/* C6.3.41 FADD (scalar) */
+#define arm_format_fadd_scalar(p, type, rd, rn, rm) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rm) << 16) | (0x1 << 13) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_fadd_d(p, rd, rn, rm) arm_format_fadd_scalar ((p), 0x1, (rd), (rn), (rm))
+#define arm_fadd_s(p, rd, rn, rm) arm_format_fadd_scalar ((p), 0x0, (rd), (rn), (rm))
+
+/* C6.3.149 FSUB (scalar) */
+#define arm_format_fsub_scalar(p, type, rd, rn, rm) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rm) << 16) | (0x1 << 13) | (0x1 << 12) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_fsub_d(p, rd, rn, rm) arm_format_fsub_scalar ((p), 0x1, (rd), (rn), (rm))
+#define arm_fsub_s(p, rd, rn, rm) arm_format_fsub_scalar ((p), 0x0, (rd), (rn), (rm))
+
+/* C6.3.119 FMUL (scalar) */
+#define arm_format_fmul_scalar(p, type, rd, rn, rm) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rm) << 16) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_fmul_d(p, rd, rn, rm) arm_format_fmul_scalar ((p), 0x1, (rd), (rn), (rm))
+#define arm_fmul_s(p, rd, rn, rm) arm_format_fmul_scalar ((p), 0x0, (rd), (rn), (rm))
+
+/* C6.3.86 FDIV (scalar) */
+#define arm_format_fdiv_scalar(p, type, rd, rn, rm) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((rm) << 16) | (0x1 << 12) | (0x2 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_fdiv_d(p, rd, rn, rm) arm_format_fdiv_scalar ((p), 0x1, (rd), (rn), (rm))
+#define arm_fdiv_s(p, rd, rn, rm) arm_format_fdiv_scalar ((p), 0x0, (rd), (rn), (rm))
+
+/* C6.3.116 FMSUB */
+#define arm_format_fmsub(p, type, rd, rn, rm, ra) arm_emit ((p), (0x1f << 24) | ((type) << 22) | (0x0 << 21) | ((rm) << 16) | (0x1 << 15) | ((ra) << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_fmsub_d(p, rd, rn, rm, ra) arm_format_fmsub ((p), 0x1, (rd), (rn), (rm), (ra))
+
+/* C6.3.123 FNEG */
+#define arm_format_fneg(p, type, rd, rn) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | (0x2 << 15) | (0x10 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_fneg_d(p, rd, rn) arm_format_fneg ((p), 0x1, (rd), (rn))
+#define arm_fneg_s(p, rd, rn) arm_format_fneg ((p), 0x0, (rd), (rn))
+
+/* C6.3.37 FABS (scalar) */
+#define arm_format_fabs(p, type, opc, rd, rn) arm_emit ((p), (0x1e << 24) | ((type) << 22) | (0x1 << 21) | ((opc) << 15) | (0x10 << 10) | ((rn) << 5) | ((rd) << 0))
+
+#define arm_fabs_d(p, rd, rn) arm_format_fabs ((p), 0x1, 0x1, (rd), (rn))
+
+/* C5.6.60 DMB */
+#define arm_format_dmb(p, opc, CRm) arm_emit ((p), (0x354 << 22) | (0x3 << 16) | (0x3 << 12) | ((CRm) << 8) | (0x1 << 7) | ((opc) << 5) | (0x1f << 0))
+
+#define ARM_DMB_LD 0x1
+#define ARM_DMB_ST 0x2
+#define ARM_DMB_ALL 0x3
+#define ARM_DMB_SY 0xc
+
+#define arm_dmb(p, imm) arm_format_dmb ((p), 0x1, (imm))
+
+/* C5.6.129 MRS */
+
+#define ARM_MRS_REG_TPIDR_EL0 0x5e82
+
+#define arm_format_mrs(p, sysreg, rt) arm_emit ((p), (0x354 << 22) | (0x1 << 21) | (0x1 << 20) | ((sysreg) << 5) | ((rt) << 0))
+
+#define arm_mrs(p, rt, sysreg) arm_format_mrs ((p), (sysreg), (rt))
+
+#endif /* __arm_CODEGEN_H__ */
--- mono-4.2.1.102+dfsg2.orig/mono/metadata/assembly.c
+++ mono-4.2.1.102+dfsg2/mono/metadata/assembly.c
@@ -105,7 +105,6 @@ static const AssemblyVersionMap framewor
 	{"Mono.Security.Win32", 0},
 	{"Mono.Xml.Ext", 0},
 	{"Novell.Directory.Ldap", 0},
-	{"Npgsql", 0},
 	{"PEAPI", 0},
 	{"System", 0},
 	{"System.ComponentModel.Composition", 2},
--- mono-4.2.1.102+dfsg2.orig/mono/mini/Makefile.am
+++ mono-4.2.1.102+dfsg2/mono/mini/Makefile.am
@@ -181,7 +181,7 @@ buildver-boehm.h: libmini-static.la $(mo
 else
 buildver-boehm.h: libmini-static.la $(monodir)/mono/metadata/libmonoruntime-static.la
 endif
-	@echo "const char *build_date = \"`date`\";" > buildver-boehm.h
+	@echo "const char *build_date;" > buildver-boehm.h
 mono_boehm-main.$(OBJEXT): buildver-boehm.h
 endif
 
@@ -190,7 +190,7 @@ buildver-sgen.h: libmini-static.la $(mon
 else
 buildver-sgen.h: libmini-static.la $(monodir)/mono/metadata/libmonoruntimesgen-static.la $(monodir)/mono/sgen/libmonosgen-static.la
 endif
-	@echo "const char *build_date = \"`date`\";" > buildver-sgen.h
+	@echo "const char *build_date;" > buildver-sgen.h
 mono_sgen-main-sgen.$(OBJEXT): buildver-sgen.h
 main-sgen.$(OBJEXT): buildver-sgen.h
 
@@ -749,7 +749,7 @@ EXTRA_DIST = TestDriver.cs \
 	Makefile.am.in
 
 version.h: Makefile
-	echo "#define FULL_VERSION \"Stable 4.2.1.102/6dd2d0d\"" > version.h
+	echo "#define FULL_VERSION \"Debian $$(dpkg-parsechangelog -l$(top_srcdir)/debian/changelog | grep ^Vers | cut -d\  -f2)\"" > version.h
 
 # Utility target for patching libtool to speed up linking
 patch-libtool:
--- mono-4.2.1.102+dfsg2.orig/mono/mini/aot-compiler.c
+++ mono-4.2.1.102+dfsg2/mono/mini/aot-compiler.c
@@ -836,7 +836,329 @@ arch_init (MonoAotCompile *acfg)
 
 #ifdef TARGET_ARM64
 
-#include "../../../mono-extensions/mono/mini/aot-compiler-arm64.c"
+
+/* Load the contents of GOT_SLOT into dreg, clobbering ip0 */
+static void
+arm64_emit_load_got_slot (MonoAotCompile *acfg, int dreg, int got_slot)
+{
+	int offset;
+
+	g_assert (acfg->fp);
+	emit_unset_mode (acfg);
+	/* r16==ip0 */
+	offset = (int)(got_slot * sizeof (gpointer));
+#ifdef TARGET_MACH
+	/* clang's integrated assembler */
+	fprintf (acfg->fp, "adrp x16, %s@PAGE+%d\n", acfg->got_symbol, offset & 0xfffff000);
+	fprintf (acfg->fp, "add x16, x16, %s@PAGEOFF\n", acfg->got_symbol);
+	fprintf (acfg->fp, "ldr x%d, [x16, #%d]\n", dreg, offset & 0xfff);
+#else
+	/* Linux GAS */
+	fprintf (acfg->fp, "adrp x16, %s+%d\n", acfg->got_symbol, offset & 0xfffff000);
+	fprintf (acfg->fp, "add x16, x16, :lo12:%s\n", acfg->got_symbol);
+	fprintf (acfg->fp, "ldr x%d, [x16, %d]\n", dreg, offset & 0xfff);
+#endif
+}
+
+static void
+arm64_emit_objc_selector_ref (MonoAotCompile *acfg, guint8 *code, int index, int *code_size)
+{
+	int reg;
+
+	g_assert (acfg->fp);
+	emit_unset_mode (acfg);
+
+	/* ldr rt, target */
+	reg = arm_get_ldr_lit_reg (code);
+
+	fprintf (acfg->fp, "adrp x%d, L_OBJC_SELECTOR_REFERENCES_%d@PAGE\n", reg, index);
+	fprintf (acfg->fp, "add x%d, x%d, L_OBJC_SELECTOR_REFERENCES_%d@PAGEOFF\n", reg, reg, index);
+	fprintf (acfg->fp, "ldr x%d, [x%d]\n", reg, reg);
+
+	*code_size = 12;
+}
+
+static void
+arm64_emit_direct_call (MonoAotCompile *acfg, const char *target, gboolean external, gboolean thumb, MonoJumpInfo *ji, int *call_size)
+{
+	g_assert (acfg->fp);
+	emit_unset_mode (acfg);
+	if (ji && ji->relocation == MONO_R_ARM64_B) {
+		fprintf (acfg->fp, "b %s\n", target);
+	} else {
+		if (ji)
+			g_assert (ji->relocation == MONO_R_ARM64_BL);
+		fprintf (acfg->fp, "bl %s\n", target);
+	}
+	*call_size = 4;
+}
+
+static void
+arm64_emit_got_access (MonoAotCompile *acfg, guint8 *code, int got_slot, int *code_size)
+{
+	int reg;
+
+	/* ldr rt, target */
+	reg = arm_get_ldr_lit_reg (code);
+	arm64_emit_load_got_slot (acfg, reg, got_slot);
+	*code_size = 12;
+}
+
+static void
+arm64_emit_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int offset, int info_offset)
+{
+	arm64_emit_load_got_slot (acfg, ARMREG_R16, offset / sizeof (gpointer));
+	fprintf (acfg->fp, "br x16\n");
+	/* Used by mono_aot_get_plt_info_offset () */
+	fprintf (acfg->fp, "%s %d\n", acfg->inst_directive, info_offset);
+}
+
+static void
+arm64_emit_tramp_page_common_code (MonoAotCompile *acfg, int pagesize, int arg_reg, int *size)
+{
+	guint8 buf [256];
+	guint8 *code;
+	int imm;
+
+	/* The common code */
+	code = buf;
+	imm = pagesize;
+	/* The trampoline address is in IP0 */
+	arm_movzx (code, ARMREG_IP1, imm & 0xffff, 0);
+	arm_movkx (code, ARMREG_IP1, (imm >> 16) & 0xffff, 16);
+	/* Compute the data slot address */
+	arm_subx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
+	/* Trampoline argument */
+	arm_ldrx (code, arg_reg, ARMREG_IP0, 0);
+	/* Address */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 8);
+	arm_brx (code, ARMREG_IP0);
+
+	/* Emit it */
+	emit_code_bytes (acfg, buf, code - buf);
+
+	*size = code - buf;
+}
+
+static void
+arm64_emit_tramp_page_specific_code (MonoAotCompile *acfg, int pagesize, int common_tramp_size, int specific_tramp_size)
+{
+	guint8 buf [256];
+	guint8 *code;
+	int i, count;
+
+	count = (pagesize - common_tramp_size) / specific_tramp_size;
+	for (i = 0; i < count; ++i) {
+		code = buf;
+		arm_adrx (code, ARMREG_IP0, code);
+		/* Branch to the generic code */
+		arm_b (code, code - 4 - (i * specific_tramp_size) - common_tramp_size);
+		/* This has to be 2 pointers long */
+		arm_nop (code);
+		arm_nop (code);
+		g_assert (code - buf == specific_tramp_size);
+		emit_code_bytes (acfg, buf, code - buf);
+	}
+}
+
+static void
+arm64_emit_specific_trampoline_pages (MonoAotCompile *acfg)
+{
+	guint8 buf [128];
+	guint8 *code;
+	guint8 *labels [16];
+	int common_tramp_size;
+	int specific_tramp_size = 2 * 8;
+	int imm, pagesize;
+	char symbol [128];
+
+	if (!acfg->aot_opts.use_trampolines_page)
+		return;
+
+#ifdef TARGET_MACH
+	/* Have to match the target pagesize */
+	pagesize = 16384;
+#else
+	pagesize = mono_pagesize ();
+#endif
+	acfg->tramp_page_size = pagesize;
+
+	/* The specific trampolines */
+	sprintf (symbol, "%sspecific_trampolines_page", acfg->user_symbol_prefix);
+	emit_alignment (acfg, pagesize);
+	emit_global (acfg, symbol, TRUE);
+	emit_label (acfg, symbol);
+
+	/* The common code */
+	arm64_emit_tramp_page_common_code (acfg, pagesize, ARMREG_IP1, &common_tramp_size);
+	acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_SPECIFIC] = common_tramp_size;
+
+	arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size);
+
+	/* The rgctx trampolines */
+	/* These are the same as the specific trampolines, but they load the argument into MONO_ARCH_RGCTX_REG */
+	sprintf (symbol, "%srgctx_trampolines_page", acfg->user_symbol_prefix);
+	emit_alignment (acfg, pagesize);
+	emit_global (acfg, symbol, TRUE);
+	emit_label (acfg, symbol);
+
+	/* The common code */
+	arm64_emit_tramp_page_common_code (acfg, pagesize, MONO_ARCH_RGCTX_REG, &common_tramp_size);
+	acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_STATIC_RGCTX] = common_tramp_size;
+
+	arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size);
+
+	/* The gsharedvt arg trampolines */
+	/* These are the same as the specific trampolines */
+	sprintf (symbol, "%sgsharedvt_arg_trampolines_page", acfg->user_symbol_prefix);
+	emit_alignment (acfg, pagesize);
+	emit_global (acfg, symbol, TRUE);
+	emit_label (acfg, symbol);
+
+	arm64_emit_tramp_page_common_code (acfg, pagesize, ARMREG_IP1, &common_tramp_size);
+	acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_GSHAREDVT_ARG] = common_tramp_size;
+
+	arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size);
+
+	/* The IMT trampolines */
+	sprintf (symbol, "%simt_trampolines_page", acfg->user_symbol_prefix);
+	emit_alignment (acfg, pagesize);
+	emit_global (acfg, symbol, TRUE);
+	emit_label (acfg, symbol);
+
+	code = buf;
+	imm = pagesize;
+	/* The trampoline address is in IP0 */
+	arm_movzx (code, ARMREG_IP1, imm & 0xffff, 0);
+	arm_movkx (code, ARMREG_IP1, (imm >> 16) & 0xffff, 16);
+	/* Compute the data slot address */
+	arm_subx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
+	/* Trampoline argument */
+	arm_ldrx (code, ARMREG_IP1, ARMREG_IP0, 0);
+
+	/* Same as arch_emit_imt_thunk () */
+	labels [0] = code;
+	arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 0);
+	arm_cmpx (code, ARMREG_IP0, MONO_ARCH_RGCTX_REG);
+	labels [1] = code;
+	arm_bcc (code, ARMCOND_EQ, 0);
+
+	/* End-of-loop check */
+	labels [2] = code;
+	arm_cbzx (code, ARMREG_IP0, 0);
+
+	/* Loop footer */
+	arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 2 * 8);
+	arm_b (code, labels [0]);
+
+	/* Match */
+	mono_arm_patch (labels [1], code, MONO_R_ARM64_BCC);
+	/* Load vtable slot addr */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 8);
+	/* Load vtable slot */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 0);
+	arm_brx (code, ARMREG_IP0);
+
+	/* No match */
+	mono_arm_patch (labels [2], code, MONO_R_ARM64_CBZ);
+	/* Load fail addr */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 8);
+	arm_brx (code, ARMREG_IP0);
+
+	emit_code_bytes (acfg, buf, code - buf);
+
+	common_tramp_size = code - buf;
+	acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_IMT_THUNK] = common_tramp_size;
+
+	arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size);
+}
+
+static void
+arm64_emit_specific_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
+{
+	/* Load argument from second GOT slot */
+	arm64_emit_load_got_slot (acfg, ARMREG_R17, offset + 1);
+	/* Load generic trampoline address from first GOT slot */
+	arm64_emit_load_got_slot (acfg, ARMREG_R16, offset);
+	fprintf (acfg->fp, "br x16\n");
+	*tramp_size = 7 * 4;
+}
+
+static void
+arm64_emit_unbox_trampoline (MonoAotCompile *acfg, MonoCompile *cfg, MonoMethod *method, const char *call_target)
+{
+	emit_unset_mode (acfg);
+	fprintf (acfg->fp, "add x0, x0, %d\n", (int)(sizeof (MonoObject)));
+	fprintf (acfg->fp, "b %s\n", call_target);
+}
+
+static void
+arm64_emit_static_rgctx_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
+{
+	/* Similar to the specific trampolines, but use the rgctx reg instead of ip1 */
+
+	/* Load argument from first GOT slot */
+	arm64_emit_load_got_slot (acfg, MONO_ARCH_RGCTX_REG, offset);
+	/* Load generic trampoline address from second GOT slot */
+	arm64_emit_load_got_slot (acfg, ARMREG_R16, offset + 1);
+	fprintf (acfg->fp, "br x16\n");
+	*tramp_size = 7 * 4;
+}
+
+static void
+arm64_emit_imt_thunk (MonoAotCompile *acfg, int offset, int *tramp_size)
+{
+	guint8 buf [128];
+	guint8 *code, *labels [16];
+
+	/* Load parameter from GOT slot into ip1 */
+	arm64_emit_load_got_slot (acfg, ARMREG_R17, offset);
+
+	code = buf;
+	labels [0] = code;
+	arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 0);
+	arm_cmpx (code, ARMREG_IP0, MONO_ARCH_RGCTX_REG);
+	labels [1] = code;
+	arm_bcc (code, ARMCOND_EQ, 0);
+
+	/* End-of-loop check */
+	labels [2] = code;
+	arm_cbzx (code, ARMREG_IP0, 0);
+
+	/* Loop footer */
+	arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 2 * 8);
+	arm_b (code, labels [0]);
+
+	/* Match */
+	mono_arm_patch (labels [1], code, MONO_R_ARM64_BCC);
+	/* Load vtable slot addr */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 8);
+	/* Load vtable slot */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 0);
+	arm_brx (code, ARMREG_IP0);
+
+	/* No match */
+	mono_arm_patch (labels [2], code, MONO_R_ARM64_CBZ);
+	/* Load fail addr */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_IP1, 8);
+	arm_brx (code, ARMREG_IP0);
+
+	emit_code_bytes (acfg, buf, code - buf);
+
+	*tramp_size = code - buf + (3 * 4);
+}
+
+static void
+arm64_emit_gsharedvt_arg_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
+{
+	/* Similar to the specific trampolines, but the address is in the second slot */
+	/* Load argument from first GOT slot */
+	arm64_emit_load_got_slot (acfg, ARMREG_R17, offset);
+	/* Load generic trampoline address from second GOT slot */
+	arm64_emit_load_got_slot (acfg, ARMREG_R16, offset + 1);
+	fprintf (acfg->fp, "br x16\n");
+	*tramp_size = 7 * 4;
+}
 
 #endif
 
--- mono-4.2.1.102+dfsg2.orig/mono/mini/cpu-ppc.md
+++ mono-4.2.1.102+dfsg2/mono/mini/cpu-ppc.md
@@ -128,6 +128,8 @@ storer8_memindex: dest:b src1:i src2:i l
 loadu4_mem: dest:i len:8
 move: dest:i src1:i len:4
 fmove: dest:f src1:f len:4
+move_f_to_i4: dest:i src1:f len:8
+move_i4_to_f: dest:f src1:i len:8
 add_imm: dest:i src1:i len:4
 sub_imm: dest:i src1:i len:4
 mul_imm: dest:i src1:i len:4
@@ -314,5 +316,5 @@ vcall2_membase: src1:b len:16 clob:c
 
 jump_table: dest:i len:8
 
-atomic_add_i4: src1:b src2:i dest:i len:20
+atomic_add_i4: src1:b src2:i dest:i len:28
 atomic_cas_i4: src1:b src2:i src3:i dest:i len:38
--- mono-4.2.1.102+dfsg2.orig/mono/mini/exceptions-arm64.c
+++ mono-4.2.1.102+dfsg2/mono/mini/exceptions-arm64.c
@@ -1 +1,585 @@
-#include "../../../mono-extensions/mono/mini/exceptions-arm64.c"
+/*
+ * exceptions-arm64.c: exception support for ARM64
+ *
+ * Copyright 2013 Xamarin Inc
+ *
+ * Based on exceptions-arm.c:
+ *
+ * Authors:
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *   Paolo Molaro (lupus@ximian.com)
+ *
+ * (C) 2001 Ximian, Inc.
+ */
+
+#include "mini.h"
+
+#include <mono/arch/arm64/arm64-codegen.h>
+#include <mono/metadata/abi-details.h>
+
+#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
+
+#ifndef DISABLE_JIT
+
+gpointer
+mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
+{
+	guint8 *start, *code;
+	MonoJumpInfo *ji = NULL;
+	GSList *unwind_ops = NULL;
+	int i, ctx_reg, size;
+
+	size = 256;
+	code = start = mono_global_codeman_reserve (size);
+
+	arm_movx (code, ARMREG_IP0, ARMREG_R0);
+	ctx_reg = ARMREG_IP0;
+	/* Restore fregs */
+	for (i = 0; i < 32; ++i)
+		arm_ldrfpx (code, i, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, fregs) + (i * 8));
+	/* Restore gregs */
+	// FIXME: Restore less registers
+	// FIXME: fp should be restored later
+	code = mono_arm_emit_load_regarray (code, 0xffffffff & ~(1 << ctx_reg) & ~(1 << ARMREG_SP), ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs));
+	/* ip0/ip1 doesn't need to be restored */
+	/* ip1 = pc */
+	arm_ldrx (code, ARMREG_IP1, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, pc));
+	/* ip0 = sp */
+	arm_ldrx (code, ARMREG_IP0, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_SP * 8));
+	/* Restore sp, ctx is no longer valid */
+	arm_movspx (code, ARMREG_SP, ARMREG_IP0); 
+	/* Branch to pc */
+	arm_brx (code, ARMREG_IP1);
+	/* Not reached */
+	arm_brk (code, 0);
+
+	g_assert ((code - start) < size);
+	mono_arch_flush_icache (start, code - start);
+	mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
+
+	if (info)
+		*info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops);
+
+	return start;
+}
+
+gpointer
+mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
+{
+	guint8 *code;
+	guint8* start;
+	int size, offset, gregs_offset, fregs_offset, ctx_offset, num_fregs, frame_size;
+	MonoJumpInfo *ji = NULL;
+	GSList *unwind_ops = NULL;
+
+	size = 512;
+	start = code = mono_global_codeman_reserve (size);
+
+	/* Compute stack frame size and offsets */
+	offset = 0;
+	/* frame block */
+	offset += 2 * 8;
+	/* gregs */
+	gregs_offset = offset;
+	offset += 32 * 8;
+	/* fregs */
+	num_fregs = 8;
+	fregs_offset = offset;
+	offset += num_fregs * 8;
+	ctx_offset = offset;
+	ctx_offset += 8;
+	frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
+
+	/*
+	 * We are being called from C code, ctx is in r0, the address to call is in r1.
+	 * We need to save state, restore ctx, make the call, then restore the previous state,
+	 * returning the value returned by the call.
+	 */
+
+	/* Setup a frame */
+	arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -frame_size);
+	arm_movspx (code, ARMREG_FP, ARMREG_SP);
+
+	/* Save ctx */
+	arm_strx (code, ARMREG_R0, ARMREG_FP, ctx_offset);
+	/* Save gregs */
+	code = mono_arm_emit_store_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS | (1 << ARMREG_FP), ARMREG_FP, gregs_offset);
+	/* No need to save/restore fregs, since we don't currently use them */
+
+	/* Load regs from ctx */
+	code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, regs));
+	/* Load fp */
+	arm_ldrx (code, ARMREG_FP, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_FP * 8));
+
+	/* Make the call */
+	arm_blrx (code, ARMREG_R1);
+	/* For filters, the result is in R0 */
+
+	/* Restore fp */
+	arm_ldrx (code, ARMREG_FP, ARMREG_SP, gregs_offset + (ARMREG_FP * 8));
+	/* Load ctx */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_FP, ctx_offset);
+	/* Save registers back to ctx */
+	/* This isn't strictly neccessary since we don't allocate variables used in eh clauses to registers */
+	code = mono_arm_emit_store_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoContext, regs));
+
+	/* Restore regs */
+	code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_FP, gregs_offset);
+	/* Destroy frame */
+	code = mono_arm_emit_destroy_frame (code, frame_size, (1 << ARMREG_IP0));
+	arm_retx (code, ARMREG_LR);
+
+	g_assert ((code - start) < size);
+	mono_arch_flush_icache (start, code - start);
+	mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
+
+	if (info)
+		*info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops);
+
+	return start;
+}
+
+static gpointer 
+get_throw_trampoline (int size, gboolean corlib, gboolean rethrow, gboolean llvm, gboolean resume_unwind, const char *tramp_name, MonoTrampInfo **info, gboolean aot)
+{
+	guint8 *start, *code;
+	MonoJumpInfo *ji = NULL;
+	GSList *unwind_ops = NULL;
+	int i, offset, gregs_offset, fregs_offset, frame_size, num_fregs;
+
+	code = start = mono_global_codeman_reserve (size);
+
+	/* We are being called by JITted code, the exception object/type token is in R0 */
+
+	/* Compute stack frame size and offsets */
+	offset = 0;
+	/* frame block */
+	offset += 2 * 8;
+	/* gregs */
+	gregs_offset = offset;
+	offset += 32 * 8;
+	/* fregs */
+	num_fregs = 8;
+	fregs_offset = offset;
+	offset += num_fregs * 8;
+	frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
+
+	/* Setup a frame */
+	arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -frame_size);
+	arm_movspx (code, ARMREG_FP, ARMREG_SP);
+
+	/* Save gregs */
+	code = mono_arm_emit_store_regarray (code, 0xffffffff, ARMREG_FP, gregs_offset);
+	if (corlib && !llvm)
+		/* The real LR is in R1 */
+		arm_strx (code, ARMREG_R1, ARMREG_FP, gregs_offset + (ARMREG_LR * 8));
+	/* Save fp/sp */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_FP, 0);
+	arm_strx (code, ARMREG_IP0, ARMREG_FP, gregs_offset + (ARMREG_FP * 8));
+	arm_addx_imm (code, ARMREG_IP0, ARMREG_FP, frame_size);
+	arm_strx (code, ARMREG_IP0, ARMREG_FP, gregs_offset + (ARMREG_SP * 8));	
+	/* Save fregs */
+	for (i = 0; i < num_fregs; ++i)
+		arm_strfpx (code, ARMREG_D8 + i, ARMREG_FP, fregs_offset + (i * 8));
+
+	/* Call the C trampoline function */
+	/* Arg1 =  exception object/type token */
+	arm_movx (code, ARMREG_R0, ARMREG_R0);
+	/* Arg2 = caller ip */
+	if (corlib) {
+		if (llvm)
+			arm_ldrx (code, ARMREG_R1, ARMREG_FP, gregs_offset + (ARMREG_LR * 8));
+		else
+			arm_movx (code, ARMREG_R1, ARMREG_R1);
+	} else {
+		arm_ldrx (code, ARMREG_R1, ARMREG_FP, 8);
+	}
+	/* Arg 3 = gregs */
+	arm_addx_imm (code, ARMREG_R2, ARMREG_FP, gregs_offset);
+	/* Arg 4 = fregs */
+	arm_addx_imm (code, ARMREG_R3, ARMREG_FP, fregs_offset);
+	/* Arg 5 = corlib */
+	arm_movzx (code, ARMREG_R4, corlib ? 1 : 0, 0);
+	/* Arg 6 = rethrow */
+	arm_movzx (code, ARMREG_R5, rethrow ? 1 : 0, 0);
+	/* Call the function */
+	if (aot) {
+		const char *icall_name;
+
+		if (resume_unwind)
+			icall_name = "mono_arm_resume_unwind";
+		else
+			icall_name = "mono_arm_throw_exception";
+
+		code = mono_arm_emit_aotconst (&ji, code, start, ARMREG_LR, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
+	} else {
+		gpointer icall_func;
+
+		if (resume_unwind)
+			icall_func = mono_arm_resume_unwind;
+		else
+			icall_func = mono_arm_throw_exception;
+
+		code = mono_arm_emit_imm64 (code, ARMREG_LR, (guint64)icall_func);
+	}
+	arm_blrx (code, ARMREG_LR);
+	/* This shouldn't return */
+	arm_brk (code, 0x0);
+
+	g_assert ((code - start) < size);
+	mono_arch_flush_icache (start, code - start);
+	mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);
+
+	if (info)
+		*info = mono_tramp_info_create (tramp_name, start, code - start, ji, unwind_ops);
+
+	return start;
+}
+
+gpointer 
+mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
+{
+	return get_throw_trampoline (256, FALSE, FALSE, FALSE, FALSE, "throw_exception", info, aot);
+}
+
+gpointer
+mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
+{
+	return get_throw_trampoline (256, FALSE, TRUE, FALSE, FALSE, "rethrow_exception", info, aot);
+}
+
+gpointer 
+mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
+{
+	return get_throw_trampoline (256, TRUE, FALSE, FALSE, FALSE, "throw_corlib_exception", info, aot);
+}
+
+GSList*
+mono_arm_get_exception_trampolines (gboolean aot)
+{
+	MonoTrampInfo *info;
+	GSList *tramps = NULL;
+
+	/* LLVM uses the normal trampolines, but with a different name */
+	get_throw_trampoline (256, TRUE, FALSE, FALSE, FALSE, "llvm_throw_corlib_exception_trampoline", &info, aot);
+	tramps = g_slist_prepend (tramps, info);
+	
+	get_throw_trampoline (256, TRUE, FALSE, TRUE, FALSE, "llvm_throw_corlib_exception_abs_trampoline", &info, aot);
+	tramps = g_slist_prepend (tramps, info);
+
+	get_throw_trampoline (256, FALSE, FALSE, FALSE, TRUE, "llvm_resume_unwind_trampoline", &info, aot);
+	tramps = g_slist_prepend (tramps, info);
+
+	return tramps;
+}
+
+#else /* DISABLE_JIT */
+
+gpointer
+mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+gpointer
+mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+gpointer 
+mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+gpointer
+mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+gpointer 
+mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
+{
+	g_assert_not_reached ();
+	return NULL;
+}	
+
+GSList*
+mono_arm_get_exception_trampolines (gboolean aot)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+#endif /* !DISABLE_JIT */
+
+void
+mono_arch_exceptions_init (void)
+{
+	guint8 *tramp;
+	GSList *tramps, *l;
+
+	if (mono_aot_only) {
+		tramp = mono_aot_get_trampoline ("llvm_throw_corlib_exception_trampoline");
+		mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_trampoline", NULL, TRUE);
+		tramp = mono_aot_get_trampoline ("llvm_throw_corlib_exception_abs_trampoline");
+		mono_register_jit_icall (tramp, "llvm_throw_corlib_exception_abs_trampoline", NULL, TRUE);
+		tramp = mono_aot_get_trampoline ("llvm_resume_unwind_trampoline");
+		mono_register_jit_icall (tramp, "llvm_resume_unwind_trampoline", NULL, TRUE);
+	} else {
+		tramps = mono_arm_get_exception_trampolines (FALSE);
+		for (l = tramps; l; l = l->next) {
+			MonoTrampInfo *info = l->data;
+
+			mono_register_jit_icall (info->code, g_strdup (info->name), NULL, TRUE);
+			mono_tramp_info_register (info);
+		}
+		g_slist_free (tramps);
+	}
+}
+
+/*
+ * mono_arm_throw_exception:
+ *
+ *   This function is called by the exception trampolines.
+ * FP_REGS points to the 8 callee saved fp regs.
+ */
+void
+mono_arm_throw_exception (gpointer arg, mgreg_t pc, mgreg_t *int_regs, gdouble *fp_regs, gboolean corlib, gboolean rethrow)
+{
+	MonoContext ctx;
+	MonoObject *exc = NULL;
+	guint32 ex_token_index, ex_token;
+
+	if (!corlib)
+		exc = arg;
+	else {
+		ex_token_index = (guint64)arg;
+		ex_token = MONO_TOKEN_TYPE_DEF | ex_token_index;
+		exc = (MonoObject*)mono_exception_from_token (mono_defaults.corlib, ex_token);
+	}
+
+	/* Adjust pc so it points into the call instruction */
+	pc -= 4;
+
+	/* Initialize a ctx based on the arguments */
+	memset (&ctx, 0, sizeof (MonoContext));
+	memcpy (&(ctx.regs [0]), int_regs, sizeof (mgreg_t) * 32);
+	memcpy (&(ctx.fregs [ARMREG_D8]), fp_regs, sizeof (double) * 8);
+	ctx.pc = pc;
+
+	if (mono_object_isinst (exc, mono_defaults.exception_class)) {
+		MonoException *mono_ex = (MonoException*)exc;
+		if (!rethrow)
+			mono_ex->stack_trace = NULL;
+	}
+
+	mono_handle_exception (&ctx, exc);
+
+	mono_restore_context (&ctx);
+}
+
+void
+mono_arm_resume_unwind (gpointer arg, mgreg_t pc, mgreg_t *int_regs, gdouble *fp_regs, gboolean corlib, gboolean rethrow)
+{
+	MonoContext ctx;
+
+	/* Adjust pc so it points into the call instruction */
+	pc -= 4;
+
+	/* Initialize a ctx based on the arguments */
+	memset (&ctx, 0, sizeof (MonoContext));
+	memcpy (&(ctx.regs [0]), int_regs, sizeof (mgreg_t) * 32);
+	memcpy (&(ctx.fregs [ARMREG_D8]), fp_regs, sizeof (double) * 8);
+	ctx.pc = pc;
+
+	mono_resume_unwind (&ctx);
+}
+
+/* 
+ * mono_arch_find_jit_info:
+ *
+ * See exceptions-amd64.c for docs;
+ */
+gboolean
+mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, 
+							 MonoJitInfo *ji, MonoContext *ctx, 
+							 MonoContext *new_ctx, MonoLMF **lmf,
+							 mgreg_t **save_locations,
+							 StackFrameInfo *frame)
+{
+	gpointer ip = MONO_CONTEXT_GET_IP (ctx);
+
+	memset (frame, 0, sizeof (StackFrameInfo));
+	frame->ji = ji;
+
+	*new_ctx = *ctx;
+
+	if (ji != NULL) {
+		mgreg_t regs [MONO_MAX_IREGS + 8 + 1];
+		guint8 *cfa;
+		guint32 unwind_info_len;
+		guint8 *unwind_info;
+
+		frame->type = FRAME_TYPE_MANAGED;
+
+		unwind_info = mono_jinfo_get_unwind_info (ji, &unwind_info_len);
+
+		memcpy (regs, &new_ctx->regs, sizeof (mgreg_t) * 32);
+		/* v8..v15 are callee saved */
+		memcpy (regs + MONO_MAX_IREGS, &(new_ctx->fregs [8]), sizeof (mgreg_t) * 8);
+
+		mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
+						   (guint8*)ji->code_start + ji->code_size,
+						   ip, NULL, regs, MONO_MAX_IREGS + 8,
+						   save_locations, MONO_MAX_IREGS, &cfa);
+
+		memcpy (&new_ctx->regs, regs, sizeof (mgreg_t) * 32);
+		memcpy (&(new_ctx->fregs [8]), regs + MONO_MAX_IREGS, sizeof (mgreg_t) * 8);
+
+		new_ctx->pc = regs [ARMREG_LR];
+		new_ctx->regs [ARMREG_SP] = (mgreg_t)cfa;
+
+		if (*lmf && (*lmf)->gregs [MONO_ARCH_LMF_REG_SP] && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->gregs [MONO_ARCH_LMF_REG_SP])) {
+			/* remove any unused lmf */
+			*lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
+		}
+
+		/* we substract 1, so that the IP points into the call instruction */
+		new_ctx->pc--;
+
+		return TRUE;
+	} else if (*lmf) {
+		if (((gsize)(*lmf)->previous_lmf) & 2) {
+			/* 
+			 * This LMF entry is created by the soft debug code to mark transitions to
+			 * managed code done during invokes.
+			 */
+			MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
+
+			g_assert (ext->debugger_invoke);
+
+			memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
+
+			*lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
+
+			frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
+
+			return TRUE;
+		}
+
+		frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
+
+		ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->pc, NULL);
+		if (!ji)
+			return FALSE;
+
+		g_assert (MONO_ARCH_LMF_REGS == ((0x3ff << 19) | (1 << ARMREG_FP) | (1 << ARMREG_SP)));
+		memcpy (&new_ctx->regs [ARMREG_R19], &(*lmf)->gregs [0], sizeof (mgreg_t) * 10);
+		new_ctx->regs [ARMREG_FP] = (*lmf)->gregs [MONO_ARCH_LMF_REG_FP];
+		new_ctx->regs [ARMREG_SP] = (*lmf)->gregs [MONO_ARCH_LMF_REG_SP];
+		new_ctx->pc = (*lmf)->pc;
+
+		/* we substract 1, so that the IP points into the call instruction */
+		new_ctx->pc--;
+
+		*lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+void
+mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
+{
+	mono_sigctx_to_monoctx (sigctx, mctx);
+}
+
+void
+mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
+{
+	mono_monoctx_to_sigctx (mctx, sigctx);
+}
+
+/*
+ * handle_exception:
+ *
+ *   Called by resuming from a signal handler.
+ */
+static void
+handle_signal_exception (gpointer obj)
+{
+	MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id);
+	MonoContext ctx;
+
+	memcpy (&ctx, &jit_tls->ex_ctx, sizeof (MonoContext));
+
+	mono_handle_exception (&ctx, obj);
+
+	mono_restore_context (&ctx);
+}
+
+/*
+ * This is the function called from the signal handler
+ */
+gboolean
+mono_arch_handle_exception (void *ctx, gpointer obj)
+{
+#if defined(MONO_CROSS_COMPILE)
+	g_assert_not_reached ();
+#else
+	MonoJitTlsData *jit_tls;
+	void *sigctx = ctx;
+
+	/*
+	 * Resume into the normal stack and handle the exception there.
+	 */
+	jit_tls = mono_native_tls_get_value (mono_jit_tls_id);
+
+	/* Pass the ctx parameter in TLS */
+	mono_arch_sigctx_to_monoctx (sigctx, &jit_tls->ex_ctx);
+	/* The others in registers */
+	UCONTEXT_REG_R0 (sigctx) = (gsize)obj;
+
+	UCONTEXT_REG_PC (sigctx) = (gsize)handle_signal_exception;
+	UCONTEXT_REG_SP (sigctx) = UCONTEXT_REG_SP (sigctx) - MONO_ARCH_REDZONE_SIZE;
+#endif
+
+	return TRUE;
+}
+
+gpointer
+mono_arch_ip_from_context (void *sigctx)
+{
+#ifdef MONO_CROSS_COMPILE
+	g_assert_not_reached ();
+	return NULL;
+#else
+	return (gpointer)UCONTEXT_REG_PC (sigctx);
+#endif
+}
+
+void
+mono_arch_setup_async_callback (MonoContext *ctx, void (*async_cb)(void *fun), gpointer user_data)
+{
+	NOT_IMPLEMENTED;
+}
+
+/*
+ * mono_arch_setup_resume_sighandler_ctx:
+ *
+ *   Setup CTX so execution continues at FUNC.
+ */
+void
+mono_arch_setup_resume_sighandler_ctx (MonoContext *ctx, gpointer func)
+{
+	MONO_CONTEXT_SET_IP (ctx,func);
+}
--- mono-4.2.1.102+dfsg2.orig/mono/mini/mini-arm64.c
+++ mono-4.2.1.102+dfsg2/mono/mini/mini-arm64.c
@@ -1 +1,5222 @@
-#include "../../../mono-extensions/mono/mini/mini-arm64.c"
+/*
+ * mini-arm64.c: ARM64 backend for the Mono code generator
+ *
+ * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
+ * 
+ * Based on mini-arm.c:
+ *
+ * Authors:
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2003 Ximian, Inc.
+ * Copyright 2003-2011 Novell, Inc (http://www.novell.com)
+ * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
+ */
+
+#include "mini.h"
+#include "cpu-arm64.h"
+#include "ir-emit.h"
+
+#include <mono/arch/arm64/arm64-codegen.h>
+#include <mono/utils/mono-mmap.h>
+#include <mono/utils/mono-memory-model.h>
+#include <mono/metadata/abi-details.h>
+
+/*
+ * Documentation:
+ *
+ * - ARM(R) Architecture Reference Manual, ARMv8, for ARMv8-A architecture profile (DDI0487A_a_armv8_arm.pdf)
+ * - Procedure Call Standard for the ARM 64-bit Architecture (AArch64) (IHI0055B_aapcs64.pdf)
+ * - ELF for the ARM 64-bit Architecture (IHI0056B_aaelf64.pdf)
+ *
+ * Register usage:
+ * - ip0/ip1/lr are used as temporary registers
+ * - r27 is used as the rgctx/imt register
+ * - r28 is used to access arguments passed on the stack
+ * - d15/d16 are used as fp temporary registers
+ */
+
+#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
+
+#define FP_TEMP_REG ARMREG_D16
+#define FP_TEMP_REG2 ARMREG_D15
+
+#define THUNK_SIZE (4 * 4)
+
+/* The single step trampoline */
+static gpointer ss_trampoline;
+
+/* The breakpoint trampoline */
+static gpointer bp_trampoline;
+
+static gboolean ios_abi;
+
+static __attribute__((warn_unused_result)) guint8* emit_load_regset (guint8 *code, guint64 regs, int basereg, int offset);
+
+const char*
+mono_arch_regname (int reg)
+{
+	static const char * rnames[] = {
+		"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9",
+		"r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19",
+		"r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "fp",
+		"lr", "sp"
+	};
+	if (reg >= 0 && reg < 32)
+		return rnames [reg];
+	return "unknown";
+}
+
+const char*
+mono_arch_fregname (int reg)
+{
+	static const char * rnames[] = {
+		"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9",
+		"d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19",
+		"d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29",
+		"d30", "d31"
+	};
+	if (reg >= 0 && reg < 32)
+		return rnames [reg];
+	return "unknown fp";
+}
+
+int
+mono_arch_get_argument_info (MonoGenericSharingContext *gsctx, MonoMethodSignature *csig, int param_count, MonoJitArgumentInfo *arg_info)
+{
+	NOT_IMPLEMENTED;
+	return 0;
+}
+
+#define MAX_ARCH_DELEGATE_PARAMS 7
+
+static gpointer
+get_delegate_invoke_impl (gboolean has_target, gboolean param_count, guint32 *code_size)
+{
+	guint8 *code, *start;
+
+	if (has_target) {
+		start = code = mono_global_codeman_reserve (12);
+
+		/* Replace the this argument with the target */
+		arm_ldrx (code, ARMREG_IP0, ARMREG_R0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
+		arm_ldrx (code, ARMREG_R0, ARMREG_R0, MONO_STRUCT_OFFSET (MonoDelegate, target));
+		arm_brx (code, ARMREG_IP0);
+
+		g_assert ((code - start) <= 12);
+
+		mono_arch_flush_icache (start, 12);
+	} else {
+		int size, i;
+
+		size = 8 + param_count * 4;
+		start = code = mono_global_codeman_reserve (size);
+
+		arm_ldrx (code, ARMREG_IP0, ARMREG_R0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
+		/* slide down the arguments */
+		for (i = 0; i < param_count; ++i)
+			arm_movx (code, i, i + 1);
+		arm_brx (code, ARMREG_IP0);
+
+		g_assert ((code - start) <= size);
+
+		mono_arch_flush_icache (start, size);
+	}
+
+	if (code_size)
+		*code_size = code - start;
+
+	return start;
+}
+
+/*
+ * mono_arch_get_delegate_invoke_impls:
+ *
+ *   Return a list of MonoAotTrampInfo structures for the delegate invoke impl
+ * trampolines.
+ */
+GSList*
+mono_arch_get_delegate_invoke_impls (void)
+{
+	GSList *res = NULL;
+	guint8 *code;
+	guint32 code_len;
+	int i;
+	char *tramp_name;
+
+	code = get_delegate_invoke_impl (TRUE, 0, &code_len);
+	res = g_slist_prepend (res, mono_tramp_info_create ("delegate_invoke_impl_has_target", code, code_len, NULL, NULL));
+
+	for (i = 0; i <= MAX_ARCH_DELEGATE_PARAMS; ++i) {
+		code = get_delegate_invoke_impl (FALSE, i, &code_len);
+		tramp_name = g_strdup_printf ("delegate_invoke_impl_target_%d", i);
+		res = g_slist_prepend (res, mono_tramp_info_create (tramp_name, code, code_len, NULL, NULL));
+		g_free (tramp_name);
+	}
+
+	return res;
+}
+
+gpointer
+mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target)
+{
+	guint8 *code, *start;
+
+	/*
+	 * vtypes are returned in registers, or using the dedicated r8 register, so
+	 * they can be supported by delegate invokes.
+	 */
+
+	if (has_target) {
+		static guint8* cached = NULL;
+
+		if (cached)
+			return cached;
+
+		if (mono_aot_only)
+			start = mono_aot_get_trampoline ("delegate_invoke_impl_has_target");
+		else
+			start = get_delegate_invoke_impl (TRUE, 0, NULL);
+		mono_memory_barrier ();
+		cached = start;
+		return cached;
+	} else {
+		static guint8* cache [MAX_ARCH_DELEGATE_PARAMS + 1] = {NULL};
+		int i;
+
+		if (sig->param_count > MAX_ARCH_DELEGATE_PARAMS)
+			return NULL;
+		for (i = 0; i < sig->param_count; ++i)
+			if (!mono_is_regsize_var (sig->params [i]))
+				return NULL;
+
+		code = cache [sig->param_count];
+		if (code)
+			return code;
+
+		if (mono_aot_only) {
+			char *name = g_strdup_printf ("delegate_invoke_impl_target_%d", sig->param_count);
+			start = mono_aot_get_trampoline (name);
+			g_free (name);
+		} else {
+			start = get_delegate_invoke_impl (FALSE, sig->param_count, NULL);
+		}
+		mono_memory_barrier ();
+		cache [sig->param_count] = start;
+		return start;
+	}
+
+	return NULL;
+}
+
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+	return NULL;
+}
+
+gpointer
+mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
+{
+	return (gpointer)regs [ARMREG_R0];
+}
+
+void
+mono_arch_cpu_init (void)
+{
+}
+
+void
+mono_arch_init (void)
+{
+	mono_aot_register_jit_icall ("mono_arm_throw_exception", mono_arm_throw_exception);
+	mono_aot_register_jit_icall ("mono_arm_resume_unwind", mono_arm_resume_unwind);
+
+	if (!mono_aot_only)
+		bp_trampoline = mini_get_breakpoint_trampoline ();
+
+#if defined(TARGET_IOS)
+	ios_abi = TRUE;
+#endif
+}
+
+void
+mono_arch_cleanup (void)
+{
+}
+
+guint32
+mono_arch_cpu_optimizations (guint32 *exclude_mask)
+{
+	*exclude_mask = 0;
+	return 0;
+}
+
+guint32
+mono_arch_cpu_enumerate_simd_versions (void)
+{
+	return 0;
+}
+
+void
+mono_arch_register_lowlevel_calls (void)
+{
+}
+
+void
+mono_arch_finish_init (void)
+{
+}
+
+/* The maximum length is 2 instructions */
+static guint8*
+emit_imm (guint8 *code, int dreg, int imm)
+{
+	// FIXME: Optimize this
+	if (imm < 0) {
+		gint64 limm = imm;
+		arm_movnx (code, dreg, (~limm) & 0xffff, 0);
+		arm_movkx (code, dreg, (limm >> 16) & 0xffff, 16);
+	} else {
+		arm_movzx (code, dreg, imm & 0xffff, 0);
+		if (imm >> 16)
+			arm_movkx (code, dreg, (imm >> 16) & 0xffff, 16);
+	}
+
+	return code;
+}
+
+/* The maximum length is 4 instructions */
+static guint8*
+emit_imm64 (guint8 *code, int dreg, guint64 imm)
+{
+	// FIXME: Optimize this
+	arm_movzx (code, dreg, imm & 0xffff, 0);
+	if ((imm >> 16) & 0xffff)
+		arm_movkx (code, dreg, (imm >> 16) & 0xffff, 16);
+	if ((imm >> 32) & 0xffff)
+		arm_movkx (code, dreg, (imm >> 32) & 0xffff, 32);
+	if ((imm >> 48) & 0xffff)
+		arm_movkx (code, dreg, (imm >> 48) & 0xffff, 48);
+
+	return code;
+}
+
+guint8*
+mono_arm_emit_imm64 (guint8 *code, int dreg, gint64 imm)
+{
+	return emit_imm64 (code, dreg, imm);
+}
+
+/*
+ * emit_imm_template:
+ *
+ *   Emit a patchable code sequence for constructing a 64 bit immediate.
+ */
+static guint8*
+emit_imm64_template (guint8 *code, int dreg)
+{
+	arm_movzx (code, dreg, 0, 0);
+	arm_movkx (code, dreg, 0, 16);
+	arm_movkx (code, dreg, 0, 32);
+	arm_movkx (code, dreg, 0, 48);
+
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_addw_imm (guint8 *code, int dreg, int sreg, int imm)
+{
+	if (!arm_is_arith_imm (imm)) {
+		code = emit_imm (code, ARMREG_LR, imm);
+		arm_addw (code, dreg, sreg, ARMREG_LR);
+	} else {
+		arm_addw_imm (code, dreg, sreg, imm);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_addx_imm (guint8 *code, int dreg, int sreg, int imm)
+{
+	if (!arm_is_arith_imm (imm)) {
+		code = emit_imm (code, ARMREG_LR, imm);
+		arm_addx (code, dreg, sreg, ARMREG_LR);
+	} else {
+		arm_addx_imm (code, dreg, sreg, imm);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_subw_imm (guint8 *code, int dreg, int sreg, int imm)
+{
+	if (!arm_is_arith_imm (imm)) {
+		code = emit_imm (code, ARMREG_LR, imm);
+		arm_subw (code, dreg, sreg, ARMREG_LR);
+	} else {
+		arm_subw_imm (code, dreg, sreg, imm);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_subx_imm (guint8 *code, int dreg, int sreg, int imm)
+{
+	if (!arm_is_arith_imm (imm)) {
+		code = emit_imm (code, ARMREG_LR, imm);
+		arm_subx (code, dreg, sreg, ARMREG_LR);
+	} else {
+		arm_subx_imm (code, dreg, sreg, imm);
+	}
+	return code;
+}
+
+/* Emit sp+=imm. Clobbers ip0/ip1 */
+static inline __attribute__((warn_unused_result)) guint8*
+emit_addx_sp_imm (guint8 *code, int imm)
+{
+	code = emit_imm (code, ARMREG_IP0, imm);
+	arm_movspx (code, ARMREG_IP1, ARMREG_SP);
+	arm_addx (code, ARMREG_IP1, ARMREG_IP1, ARMREG_IP0);
+	arm_movspx (code, ARMREG_SP, ARMREG_IP1);
+	return code;
+}
+
+/* Emit sp-=imm. Clobbers ip0/ip1 */
+static inline __attribute__((warn_unused_result)) guint8*
+emit_subx_sp_imm (guint8 *code, int imm)
+{
+	code = emit_imm (code, ARMREG_IP0, imm);
+	arm_movspx (code, ARMREG_IP1, ARMREG_SP);
+	arm_subx (code, ARMREG_IP1, ARMREG_IP1, ARMREG_IP0);
+	arm_movspx (code, ARMREG_SP, ARMREG_IP1);
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_andw_imm (guint8 *code, int dreg, int sreg, int imm)
+{
+	// FIXME:
+	code = emit_imm (code, ARMREG_LR, imm);
+	arm_andw (code, dreg, sreg, ARMREG_LR);
+
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_andx_imm (guint8 *code, int dreg, int sreg, int imm)
+{
+	// FIXME:
+	code = emit_imm (code, ARMREG_LR, imm);
+	arm_andx (code, dreg, sreg, ARMREG_LR);
+
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_orrw_imm (guint8 *code, int dreg, int sreg, int imm)
+{
+	// FIXME:
+	code = emit_imm (code, ARMREG_LR, imm);
+	arm_orrw (code, dreg, sreg, ARMREG_LR);
+
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_orrx_imm (guint8 *code, int dreg, int sreg, int imm)
+{
+	// FIXME:
+	code = emit_imm (code, ARMREG_LR, imm);
+	arm_orrx (code, dreg, sreg, ARMREG_LR);
+
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_eorw_imm (guint8 *code, int dreg, int sreg, int imm)
+{
+	// FIXME:
+	code = emit_imm (code, ARMREG_LR, imm);
+	arm_eorw (code, dreg, sreg, ARMREG_LR);
+
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_eorx_imm (guint8 *code, int dreg, int sreg, int imm)
+{
+	// FIXME:
+	code = emit_imm (code, ARMREG_LR, imm);
+	arm_eorx (code, dreg, sreg, ARMREG_LR);
+
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_cmpw_imm (guint8 *code, int sreg, int imm)
+{
+	if (imm == 0) {
+		arm_cmpw (code, sreg, ARMREG_RZR);
+	} else {
+		// FIXME:
+		code = emit_imm (code, ARMREG_LR, imm);
+		arm_cmpw (code, sreg, ARMREG_LR);
+	}
+
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_cmpx_imm (guint8 *code, int sreg, int imm)
+{
+	if (imm == 0) {
+		arm_cmpx (code, sreg, ARMREG_RZR);
+	} else {
+		// FIXME:
+		code = emit_imm (code, ARMREG_LR, imm);
+		arm_cmpx (code, sreg, ARMREG_LR);
+	}
+
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_strb (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_strb_imm (imm)) {
+		arm_strb (code, rt, rn, imm);
+	} else {
+		g_assert (rt != ARMREG_IP0);
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_strb_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_strh (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_strh_imm (imm)) {
+		arm_strh (code, rt, rn, imm);
+	} else {
+		g_assert (rt != ARMREG_IP0);
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_strh_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_strw (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_strw_imm (imm)) {
+		arm_strw (code, rt, rn, imm);
+	} else {
+		g_assert (rt != ARMREG_IP0);
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_strw_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_strfpw (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_strw_imm (imm)) {
+		arm_strfpw (code, rt, rn, imm);
+	} else {
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_addx (code, ARMREG_IP0, rn, ARMREG_IP0);
+		arm_strfpw (code, rt, ARMREG_IP0, 0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_strfpx (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_strx_imm (imm)) {
+		arm_strfpx (code, rt, rn, imm);
+	} else {
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_addx (code, ARMREG_IP0, rn, ARMREG_IP0);
+		arm_strfpx (code, rt, ARMREG_IP0, 0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_strx (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_strx_imm (imm)) {
+		arm_strx (code, rt, rn, imm);
+	} else {
+		g_assert (rt != ARMREG_IP0);
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_strx_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_ldrb (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_pimm12_scaled (imm, 1)) {
+		arm_ldrb (code, rt, rn, imm);
+	} else {
+		g_assert (rt != ARMREG_IP0);
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_ldrb_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_ldrsbx (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_pimm12_scaled (imm, 1)) {
+		arm_ldrsbx (code, rt, rn, imm);
+	} else {
+		g_assert (rt != ARMREG_IP0);
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_ldrsbx_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_ldrh (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_pimm12_scaled (imm, 2)) {
+		arm_ldrh (code, rt, rn, imm);
+	} else {
+		g_assert (rt != ARMREG_IP0);
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_ldrh_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_ldrshx (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_pimm12_scaled (imm, 2)) {
+		arm_ldrshx (code, rt, rn, imm);
+	} else {
+		g_assert (rt != ARMREG_IP0);
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_ldrshx_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_ldrswx (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_pimm12_scaled (imm, 4)) {
+		arm_ldrswx (code, rt, rn, imm);
+	} else {
+		g_assert (rt != ARMREG_IP0);
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_ldrswx_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_ldrw (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_pimm12_scaled (imm, 4)) {
+		arm_ldrw (code, rt, rn, imm);
+	} else {
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_ldrw_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_ldrx (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_pimm12_scaled (imm, 8)) {
+		arm_ldrx (code, rt, rn, imm);
+	} else {
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_ldrx_reg (code, rt, rn, ARMREG_IP0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_ldrfpw (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_pimm12_scaled (imm, 4)) {
+		arm_ldrfpw (code, rt, rn, imm);
+	} else {
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_addx (code, ARMREG_IP0, rn, ARMREG_IP0);
+		arm_ldrfpw (code, rt, ARMREG_IP0, 0);
+	}
+	return code;
+}
+
+static inline __attribute__((warn_unused_result)) guint8*
+emit_ldrfpx (guint8 *code, int rt, int rn, int imm)
+{
+	if (arm_is_pimm12_scaled (imm, 8)) {
+		arm_ldrfpx (code, rt, rn, imm);
+	} else {
+		g_assert (rn != ARMREG_IP0);
+		code = emit_imm (code, ARMREG_IP0, imm);
+		arm_addx (code, ARMREG_IP0, rn, ARMREG_IP0);
+		arm_ldrfpx (code, rt, ARMREG_IP0, 0);
+	}
+	return code;
+}
+
+guint8*
+mono_arm_emit_ldrx (guint8 *code, int rt, int rn, int imm)
+{
+	return emit_ldrx (code, rt, rn, imm);
+}
+
+static guint8*
+emit_call (MonoCompile *cfg, guint8* code, guint32 patch_type, gconstpointer data)
+{
+	/*
+	mono_add_patch_info_rel (cfg, code - cfg->native_code, patch_type, data, MONO_R_ARM64_IMM);
+	code = emit_imm64_template (code, ARMREG_LR);
+	arm_blrx (code, ARMREG_LR);
+	*/
+	mono_add_patch_info_rel (cfg, code - cfg->native_code, patch_type, data, MONO_R_ARM64_BL);
+	arm_bl (code, code);
+	cfg->thunk_area += THUNK_SIZE;
+	return code;
+}
+
+static guint8*
+emit_aotconst_full (MonoCompile *cfg, MonoJumpInfo **ji, guint8 *code, guint8 *start, int dreg, guint32 patch_type, gconstpointer data)
+{
+	if (cfg)
+		mono_add_patch_info (cfg, code - cfg->native_code, patch_type, data);
+	else
+		*ji = mono_patch_info_list_prepend (*ji, code - start, patch_type, data);
+	/* See arch_emit_got_access () in aot-compiler.c */
+	arm_ldrx_lit (code, dreg, 0);
+	arm_nop (code);
+	arm_nop (code);
+	return code;
+}
+
+static guint8*
+emit_aotconst (MonoCompile *cfg, guint8 *code, int dreg, guint32 patch_type, gconstpointer data)
+{
+	return emit_aotconst_full (cfg, NULL, code, NULL, dreg, patch_type, data);
+}
+
+/*
+ * mono_arm_emit_aotconst:
+ *
+ *   Emit code to load an AOT constant into DREG. Usable from trampolines.
+ */
+guint8*
+mono_arm_emit_aotconst (gpointer ji, guint8 *code, guint8 *code_start, int dreg, guint32 patch_type, gconstpointer data)
+{
+	return emit_aotconst_full (NULL, (MonoJumpInfo**)ji, code, code_start, dreg, patch_type, data);
+}
+
+static guint8*
+emit_tls_get (guint8 *code, int dreg, int tls_offset)
+{
+	arm_mrs (code, dreg, ARM_MRS_REG_TPIDR_EL0);
+	if (tls_offset < 256) {
+		arm_ldrx (code, dreg, dreg, tls_offset);
+	} else {
+		code = emit_addx_imm (code, dreg, dreg, tls_offset);
+		arm_ldrx (code, dreg, dreg, 0);
+	}
+	return code;
+}
+
+static guint8*
+emit_tls_get_reg (guint8 *code, int dreg, int offset_reg)
+{
+	g_assert (offset_reg != ARMREG_IP0);
+	arm_mrs (code, ARMREG_IP0, ARM_MRS_REG_TPIDR_EL0);
+	arm_ldrx_reg (code, dreg, ARMREG_IP0, offset_reg);
+	return code;
+}
+
+static guint8*
+emit_tls_set (guint8 *code, int sreg, int tls_offset)
+{
+	int tmpreg = ARMREG_IP0;
+
+	g_assert (sreg != tmpreg);
+	arm_mrs (code, tmpreg, ARM_MRS_REG_TPIDR_EL0);
+	if (tls_offset < 256) {
+		arm_strx (code, sreg, tmpreg, tls_offset);
+	} else {
+		code = emit_addx_imm (code, tmpreg, tmpreg, tls_offset);
+		arm_strx (code, sreg, tmpreg, 0);
+	}
+	return code;
+}
+
+
+static guint8*
+emit_tls_set_reg (guint8 *code, int sreg, int offset_reg)
+{
+	int tmpreg = ARMREG_IP0;
+
+	g_assert (sreg != tmpreg);
+	arm_mrs (code, tmpreg, ARM_MRS_REG_TPIDR_EL0);
+	arm_strx_reg (code, sreg, tmpreg, offset_reg);
+	return code;
+}
+
+/*
+ * Emits
+ * - mov sp, fp
+ * - ldrp [fp, lr], [sp], !stack_offfset
+ * Clobbers TEMP_REGS.
+ */
+__attribute__((warn_unused_result)) guint8*
+mono_arm_emit_destroy_frame (guint8 *code, int stack_offset, guint64 temp_regs)
+{
+	arm_movspx (code, ARMREG_SP, ARMREG_FP);
+
+	if (arm_is_ldpx_imm (stack_offset)) {
+		arm_ldpx_post (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, stack_offset);
+	} else {
+		arm_ldpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0);
+		/* sp += stack_offset */
+		g_assert (temp_regs & (1 << ARMREG_IP0));
+		if (temp_regs & (1 << ARMREG_IP1)) {
+			code = emit_addx_sp_imm (code, stack_offset);
+		} else {
+			int imm = stack_offset;
+
+			/* Can't use addx_sp_imm () since we can't clobber ip0/ip1 */
+			arm_addx_imm (code, ARMREG_IP0, ARMREG_SP, 0);
+			while (imm > 256) {
+				arm_addx_imm (code, ARMREG_IP0, ARMREG_IP0, 256);
+				imm -= 256;
+			}
+			arm_addx_imm (code, ARMREG_SP, ARMREG_IP0, imm);
+		}
+	}
+	return code;
+}
+
+#define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
+
+static guint8*
+emit_thunk (guint8 *code, gconstpointer target)
+{
+	guint8 *p = code;
+
+	arm_ldrx_lit (code, ARMREG_IP0, code + 8);
+	arm_brx (code, ARMREG_IP0);
+	*(guint64*)code = (guint64)target;
+
+	mono_arch_flush_icache (p, code - p);
+	return code;
+}
+
+static gpointer
+create_thunk (MonoCompile *cfg, MonoDomain *domain, guchar *code, const guchar *target)
+{
+	MonoJitInfo *ji;
+	MonoThunkJitInfo *info;
+	guint8 *thunks, *p;
+	int thunks_size;
+	guint8 *orig_target;
+	guint8 *target_thunk;
+
+	if (!domain)
+		domain = mono_domain_get ();
+
+	if (cfg) {
+		/*
+		 * This can be called multiple times during JITting,
+		 * save the current position in cfg->arch to avoid
+		 * doing a O(n^2) search.
+		 */
+		if (!cfg->arch.thunks) {
+			cfg->arch.thunks = cfg->thunks;
+			cfg->arch.thunks_size = cfg->thunk_area;
+		}
+		thunks = cfg->arch.thunks;
+		thunks_size = cfg->arch.thunks_size;
+		if (!thunks_size) {
+			g_print ("thunk failed %p->%p, thunk space=%d method %s", code, target, thunks_size, mono_method_full_name (cfg->method, TRUE));
+			g_assert_not_reached ();
+		}
+
+		g_assert (*(guint32*)thunks == 0);
+		emit_thunk (thunks, target);
+
+		cfg->arch.thunks += THUNK_SIZE;
+		cfg->arch.thunks_size -= THUNK_SIZE;
+
+		return thunks;
+	} else {
+		ji = mini_jit_info_table_find (domain, (char*)code, NULL);
+		g_assert (ji);
+		info = mono_jit_info_get_thunk_info (ji);
+		g_assert (info);
+
+		thunks = (guint8*)ji->code_start + info->thunks_offset;
+		thunks_size = info->thunks_size;
+
+		orig_target = mono_arch_get_call_target (code + 4);
+
+		mono_domain_lock (domain);
+
+		target_thunk = NULL;
+		if (orig_target >= thunks && orig_target < thunks + thunks_size) {
+			/* The call already points to a thunk, because of trampolines etc. */
+			target_thunk = orig_target;
+		} else {
+			for (p = thunks; p < thunks + thunks_size; p += THUNK_SIZE) {
+				if (((guint32*)p) [0] == 0) {
+					/* Free entry */
+					target_thunk = p;
+					break;
+				} else if (((guint64*)p) [1] == (guint64)target) {
+					/* Thunk already points to target */
+					target_thunk = p;
+					break;
+				}
+			}
+		}
+
+		//printf ("THUNK: %p %p %p\n", code, target, target_thunk);
+
+		if (!target_thunk) {
+			mono_domain_unlock (domain);
+			g_print ("thunk failed %p->%p, thunk space=%d method %s", code, target, thunks_size, cfg ? mono_method_full_name (cfg->method, TRUE) : mono_method_full_name (jinfo_get_method (ji), TRUE));
+			g_assert_not_reached ();
+		}
+
+		emit_thunk (target_thunk, target);
+
+		mono_domain_unlock (domain);
+
+		return target_thunk;
+	}
+}
+
+static void
+arm_patch_full (MonoCompile *cfg, MonoDomain *domain, guint8 *code, guint8 *target, int relocation)
+{
+	switch (relocation) {
+	case MONO_R_ARM64_B:
+		arm_b (code, target);
+		break;
+	case MONO_R_ARM64_BCC: {
+		int cond;
+
+		cond = arm_get_bcc_cond (code);
+		arm_bcc (code, cond, target);
+		break;
+	}
+	case MONO_R_ARM64_CBZ:
+		arm_set_cbz_target (code, target);
+		break;
+	case MONO_R_ARM64_IMM: {
+		guint64 imm = (guint64)target;
+		int dreg;
+
+		/* emit_imm64_template () */
+		dreg = arm_get_movzx_rd (code);
+		arm_movzx (code, dreg, imm & 0xffff, 0);
+		arm_movkx (code, dreg, (imm >> 16) & 0xffff, 16);
+		arm_movkx (code, dreg, (imm >> 32) & 0xffff, 32);
+		arm_movkx (code, dreg, (imm >> 48) & 0xffff, 48);
+		break;
+	}
+	case MONO_R_ARM64_BL:
+		if (arm_is_bl_disp (code, target)) {
+			arm_bl (code, target);
+		} else {
+			gpointer thunk;
+
+			thunk = create_thunk (cfg, domain, code, target);
+			g_assert (arm_is_bl_disp (code, thunk));
+			arm_bl (code, thunk);			
+		}
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+static void
+arm_patch_rel (guint8 *code, guint8 *target, int relocation)
+{
+	arm_patch_full (NULL, NULL, code, target, relocation);
+}
+
+void
+mono_arm_patch (guint8 *code, guint8 *target, int relocation)
+{
+	arm_patch_rel (code, target, relocation);
+}
+
+void
+mono_arch_patch_code_new (MonoCompile *cfg, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gpointer target)
+{
+	guint8 *ip;
+
+	ip = ji->ip.i + code;
+
+	switch (ji->type) {
+	case MONO_PATCH_INFO_METHOD_JUMP:
+		/* ji->relocation is not set by the caller */
+		arm_patch_rel (ip, (guint8*)target, MONO_R_ARM64_B);
+		break;
+	default:
+		arm_patch_full (cfg, domain, ip, (guint8*)target, ji->relocation);
+		break;
+	}
+}
+
+void
+mono_arch_free_jit_tls_data (MonoJitTlsData *tls)
+{
+}
+
+void
+mono_arch_flush_register_windows (void)
+{
+}
+
+MonoMethod*
+mono_arch_find_imt_method (mgreg_t *regs, guint8 *code)
+{
+	return (gpointer)regs [MONO_ARCH_RGCTX_REG];
+}
+
+MonoVTable*
+mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code)
+{
+	return (gpointer)regs [MONO_ARCH_RGCTX_REG];
+}
+
+mgreg_t
+mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
+{
+	return ctx->regs [reg];
+}
+
+void
+mono_arch_context_set_int_reg (MonoContext *ctx, int reg, mgreg_t val)
+{
+	ctx->regs [reg] = val;
+}
+
+/*
+ * mono_arch_set_target:
+ *
+ *   Set the target architecture the JIT backend should generate code for, in the form
+ * of a GNU target triplet. Only used in AOT mode.
+ */
+void
+mono_arch_set_target (char *mtriple)
+{
+	if (strstr (mtriple, "darwin") || strstr (mtriple, "ios")) {
+		ios_abi = TRUE;
+	}
+}
+
+static void
+add_general (CallInfo *cinfo, ArgInfo *ainfo, int size, gboolean sign)
+{
+	if (cinfo->gr >= PARAM_REGS) {
+		ainfo->storage = ArgOnStack;
+		if (ios_abi) {
+			/* Assume size == align */
+			cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, size);
+			ainfo->offset = cinfo->stack_usage;
+			ainfo->slot_size = size;
+			ainfo->sign = sign;
+			cinfo->stack_usage += size;
+		} else {
+			ainfo->offset = cinfo->stack_usage;
+			ainfo->slot_size = 8;
+			ainfo->sign = FALSE;
+			/* Put arguments into 8 byte aligned stack slots */
+			cinfo->stack_usage += 8;
+		}
+	} else {
+		ainfo->storage = ArgInIReg;
+		ainfo->reg = cinfo->gr;
+		cinfo->gr ++;
+	}
+}
+
+static void
+add_fp (CallInfo *cinfo, ArgInfo *ainfo, gboolean single)
+{
+	int size = single ? 4 : 8;
+
+	if (cinfo->fr >= FP_PARAM_REGS) {
+		ainfo->storage = single ? ArgOnStackR4 : ArgOnStackR8;
+		if (ios_abi) {
+			cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, size);
+			ainfo->offset = cinfo->stack_usage;
+			ainfo->slot_size = size;
+			cinfo->stack_usage += size;
+		} else {
+			ainfo->offset = cinfo->stack_usage;
+			ainfo->slot_size = 8;
+			/* Put arguments into 8 byte aligned stack slots */
+			cinfo->stack_usage += 8;
+		}
+	} else {
+		if (single)
+			ainfo->storage = ArgInFRegR4;
+		else
+			ainfo->storage = ArgInFReg;
+		ainfo->reg = cinfo->fr;
+		cinfo->fr ++;
+	}
+}
+
+static gboolean
+is_hfa (MonoType *t, int *out_nfields, int *out_esize, int *field_offsets)
+{
+	MonoClass *klass;
+	gpointer iter;
+	MonoClassField *field;
+	MonoType *ftype, *prev_ftype = NULL;
+	int i, nfields = 0;
+
+	klass = mono_class_from_mono_type (t);
+	iter = NULL;
+	while ((field = mono_class_get_fields (klass, &iter))) {
+		if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
+			continue;
+		ftype = mono_field_get_type (field);
+		ftype = mini_type_get_underlying_type (NULL, ftype);
+
+		if (MONO_TYPE_ISSTRUCT (ftype)) {
+			int nested_nfields, nested_esize;
+			int nested_field_offsets [16];
+
+			if (!is_hfa (ftype, &nested_nfields, &nested_esize, nested_field_offsets))
+				return FALSE;
+			if (nested_esize == 4)
+				ftype = &mono_defaults.single_class->byval_arg;
+			else
+				ftype = &mono_defaults.double_class->byval_arg;
+			if (prev_ftype && prev_ftype->type != ftype->type)
+				return FALSE;
+			prev_ftype = ftype;
+			for (i = 0; i < nested_nfields; ++i) {
+				if (nfields + i < 4)
+					field_offsets [nfields + i] = field->offset - sizeof (MonoObject) + nested_field_offsets [i];
+			}
+			nfields += nested_nfields;
+		} else {
+			if (!(!ftype->byref && (ftype->type == MONO_TYPE_R4 || ftype->type == MONO_TYPE_R8)))
+				return FALSE;
+			if (prev_ftype && prev_ftype->type != ftype->type)
+				return FALSE;
+			prev_ftype = ftype;
+			if (nfields < 4)
+				field_offsets [nfields] = field->offset - sizeof (MonoObject);
+			nfields ++;
+		}
+	}
+	if (nfields == 0 || nfields > 4)
+		return FALSE;
+	*out_nfields = nfields;
+	*out_esize = prev_ftype->type == MONO_TYPE_R4 ? 4 : 8;
+	return TRUE;
+}
+
+static void
+add_valuetype (CallInfo *cinfo, MonoGenericSharingContext *gsctx, ArgInfo *ainfo, MonoType *t)
+{
+	int i, size, align_size, nregs, nfields, esize;
+	int field_offsets [16];
+	guint32 align;
+
+	size = mini_type_stack_size_full (gsctx, t, &align, FALSE);
+	align_size = ALIGN_TO (size, 8);
+
+	nregs = size / 8;
+	if (is_hfa (t, &nfields, &esize, field_offsets)) {
+		/*
+		 * The struct might include nested float structs aligned at 8,
+		 * so need to keep track of the offsets of the individual fields.
+		 */
+		if (cinfo->fr + nfields <= FP_PARAM_REGS) {
+			ainfo->storage = ArgHFA;
+			ainfo->reg = cinfo->fr;
+			ainfo->nregs = nfields;
+			ainfo->size = size;
+			ainfo->esize = esize;
+			for (i = 0; i < nfields; ++i)
+				ainfo->foffsets [i] = field_offsets [i];
+			cinfo->fr += ainfo->nregs;
+		} else {
+			cinfo->fr = FP_PARAM_REGS;
+			size = ALIGN_TO (size, 8);
+			ainfo->storage = ArgVtypeOnStack;
+			ainfo->offset = cinfo->stack_usage;
+			ainfo->size = size;
+			ainfo->hfa = TRUE;
+			ainfo->nregs = nfields;
+			ainfo->esize = esize;
+			cinfo->stack_usage += size;
+		}
+		return;
+	}
+
+	if (align_size > 16) {
+		ainfo->storage = ArgVtypeByRef;
+		ainfo->size = size;
+		return;
+	}
+
+	if (cinfo->gr + nregs > PARAM_REGS) {
+		size = ALIGN_TO (size, 8);
+		ainfo->storage = ArgVtypeOnStack;
+		ainfo->offset = cinfo->stack_usage;
+		ainfo->size = size;
+		cinfo->stack_usage += size;
+		cinfo->gr = PARAM_REGS;
+	} else {
+		ainfo->storage = ArgVtypeInIRegs;
+		ainfo->reg = cinfo->gr;
+		ainfo->nregs = nregs;
+		ainfo->size = size;
+		cinfo->gr += nregs;
+	}
+}
+
+static void
+add_param (CallInfo *cinfo, MonoGenericSharingContext *gsctx, ArgInfo *ainfo, MonoType *t)
+{
+	MonoType *ptype;
+
+	ptype = mini_type_get_underlying_type (gsctx, t);
+	switch (ptype->type) {
+	case MONO_TYPE_I1:
+		add_general (cinfo, ainfo, 1, TRUE);
+		break;
+	case MONO_TYPE_BOOLEAN:
+	case MONO_TYPE_U1:
+		add_general (cinfo, ainfo, 1, FALSE);
+		break;
+	case MONO_TYPE_I2:
+		add_general (cinfo, ainfo, 2, TRUE);
+		break;
+	case MONO_TYPE_U2:
+	case MONO_TYPE_CHAR:
+		add_general (cinfo, ainfo, 2, FALSE);
+		break;
+	case MONO_TYPE_I4:
+		add_general (cinfo, ainfo, 4, TRUE);
+		break;
+	case MONO_TYPE_U4:
+		add_general (cinfo, ainfo, 4, FALSE);
+		break;
+	case MONO_TYPE_I:
+	case MONO_TYPE_U:
+	case MONO_TYPE_PTR:
+	case MONO_TYPE_FNPTR:
+	case MONO_TYPE_CLASS:
+	case MONO_TYPE_OBJECT:
+	case MONO_TYPE_SZARRAY:
+	case MONO_TYPE_ARRAY:
+	case MONO_TYPE_STRING:
+	case MONO_TYPE_U8:
+	case MONO_TYPE_I8:
+		add_general (cinfo, ainfo, 8, FALSE);
+		break;
+	case MONO_TYPE_R8:
+		add_fp (cinfo, ainfo, FALSE);
+		break;
+	case MONO_TYPE_R4:
+		add_fp (cinfo, ainfo, TRUE);
+		break;
+	case MONO_TYPE_VALUETYPE:
+	case MONO_TYPE_TYPEDBYREF:
+		add_valuetype (cinfo, gsctx, ainfo, ptype);
+		break;
+	case MONO_TYPE_VOID:
+		ainfo->storage = ArgNone;
+		break;
+	case MONO_TYPE_GENERICINST:
+		if (!mono_type_generic_inst_is_valuetype (ptype)) {
+			add_general (cinfo, ainfo, 8, FALSE);
+		} else if (mini_is_gsharedvt_variable_type_gsctx (gsctx, ptype)) {
+			/*
+			 * Treat gsharedvt arguments as large vtypes
+			 */
+			ainfo->storage = ArgVtypeByRef;
+			ainfo->gsharedvt = TRUE;
+		} else {
+			add_valuetype (cinfo, gsctx, ainfo, ptype);
+		}
+		break;
+	case MONO_TYPE_VAR:
+	case MONO_TYPE_MVAR:
+		g_assert (mini_is_gsharedvt_type_gsctx (gsctx, ptype));
+		ainfo->storage = ArgVtypeByRef;
+		ainfo->gsharedvt = TRUE;
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+/*
+ * get_call_info:
+ *
+ *  Obtain information about a call according to the calling convention.
+ */
+static CallInfo*
+get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSignature *sig)
+{
+	CallInfo *cinfo;
+	ArgInfo *ainfo;
+	int n, pstart, pindex;
+
+	n = sig->hasthis + sig->param_count;
+
+	if (mp)
+		cinfo = mono_mempool_alloc0 (mp, sizeof (CallInfo) + (sizeof (ArgInfo) * n));
+	else
+		cinfo = g_malloc0 (sizeof (CallInfo) + (sizeof (ArgInfo) * n));
+
+	cinfo->nargs = n;
+
+	/* Return value */
+	add_param (cinfo, gsctx, &cinfo->ret, sig->ret);
+	if (cinfo->ret.storage == ArgVtypeByRef)
+		cinfo->ret.reg = ARMREG_R8;
+	/* Reset state */
+	cinfo->gr = 0;
+	cinfo->fr = 0;
+	cinfo->stack_usage = 0;
+
+	/* Parameters */
+	if (sig->hasthis)
+		add_general (cinfo, cinfo->args + 0, 8, FALSE);
+	pstart = 0;
+	for (pindex = pstart; pindex < sig->param_count; ++pindex) {
+		ainfo = cinfo->args + sig->hasthis + pindex;
+
+		if ((sig->call_convention == MONO_CALL_VARARG) && (pindex == sig->sentinelpos)) {
+			/* Prevent implicit arguments and sig_cookie from
+			   being passed in registers */
+			cinfo->gr = PARAM_REGS;
+			cinfo->fr = FP_PARAM_REGS;
+			/* Emit the signature cookie just before the implicit arguments */
+			add_param (cinfo, gsctx, &cinfo->sig_cookie, &mono_defaults.int_class->byval_arg);
+		}
+
+		add_param (cinfo, gsctx, ainfo, sig->params [pindex]);
+		if (ainfo->storage == ArgVtypeByRef) {
+			/* Pass the argument address in the next register */
+			if (cinfo->gr >= PARAM_REGS) {
+				ainfo->storage = ArgVtypeByRefOnStack;
+				ainfo->offset = cinfo->stack_usage;
+				cinfo->stack_usage += 8;
+			} else {
+				ainfo->reg = cinfo->gr;
+				cinfo->gr ++;
+			}
+		}
+	}
+
+	/* Handle the case where there are no implicit arguments */
+	if ((sig->call_convention == MONO_CALL_VARARG) && (pindex == sig->sentinelpos)) {
+		/* Prevent implicit arguments and sig_cookie from
+		   being passed in registers */
+		cinfo->gr = PARAM_REGS;
+		cinfo->fr = FP_PARAM_REGS;
+		/* Emit the signature cookie just before the implicit arguments */
+		add_param (cinfo, gsctx, &cinfo->sig_cookie, &mono_defaults.int_class->byval_arg);
+	}
+
+	cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, MONO_ARCH_FRAME_ALIGNMENT);
+
+	return cinfo;
+}
+
+typedef struct {
+	MonoMethodSignature *sig;
+	CallInfo *cinfo;
+	MonoType *rtype;
+	MonoType **param_types;
+	int n_fpargs, n_fpret;
+} ArchDynCallInfo;
+
+static gboolean
+dyn_call_supported (CallInfo *cinfo, MonoMethodSignature *sig)
+{
+	int i;
+
+	if (sig->hasthis + sig->param_count > PARAM_REGS + DYN_CALL_STACK_ARGS)
+		return FALSE;
+
+	// FIXME: Add more cases
+	switch (cinfo->ret.storage) {
+	case ArgNone:
+	case ArgInIReg:
+	case ArgInFReg:
+	case ArgInFRegR4:
+	case ArgVtypeByRef:
+		break;
+	case ArgVtypeInIRegs:
+		if (cinfo->ret.nregs > 2)
+			return FALSE;
+		break;
+	case ArgHFA:
+		break;
+	default:
+		return FALSE;
+	}
+
+	for (i = 0; i < cinfo->nargs; ++i) {
+		ArgInfo *ainfo = &cinfo->args [i];
+
+		switch (ainfo->storage) {
+		case ArgInIReg:
+		case ArgVtypeInIRegs:
+		case ArgInFReg:
+		case ArgInFRegR4:
+		case ArgHFA:
+		case ArgVtypeByRef:
+			break;
+		case ArgOnStack:
+			if (ainfo->offset >= DYN_CALL_STACK_ARGS * sizeof (mgreg_t))
+				return FALSE;
+			break;
+		default:
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+MonoDynCallInfo*
+mono_arch_dyn_call_prepare (MonoMethodSignature *sig)
+{
+	ArchDynCallInfo *info;
+	CallInfo *cinfo;
+	int i;
+
+	cinfo = get_call_info (NULL, NULL, sig);
+
+	if (!dyn_call_supported (cinfo, sig)) {
+		g_free (cinfo);
+		return NULL;
+	}
+
+	info = g_new0 (ArchDynCallInfo, 1);
+	// FIXME: Preprocess the info to speed up start_dyn_call ()
+	info->sig = sig;
+	info->cinfo = cinfo;
+	info->rtype = mini_replace_type (sig->ret);
+	info->param_types = g_new0 (MonoType*, sig->param_count);
+	for (i = 0; i < sig->param_count; ++i)
+		info->param_types [i] = mini_replace_type (sig->params [i]);
+
+	switch (cinfo->ret.storage) {
+	case ArgInFReg:
+	case ArgInFRegR4:
+		info->n_fpret = 1;
+		break;
+	case ArgHFA:
+		info->n_fpret = cinfo->ret.nregs;
+		break;
+	default:
+		break;
+	}
+	
+	return (MonoDynCallInfo*)info;
+}
+
+void
+mono_arch_dyn_call_free (MonoDynCallInfo *info)
+{
+	ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
+
+	g_free (ainfo->cinfo);
+	g_free (ainfo->param_types);
+	g_free (ainfo);
+}
+
+static double
+bitcast_r4_to_r8 (float f)
+{
+	float *p = &f;
+
+	return *(double*)p;
+}
+
+static float
+bitcast_r8_to_r4 (double f)
+{
+	double *p = &f;
+
+	return *(float*)p;
+}
+
+void
+mono_arch_start_dyn_call (MonoDynCallInfo *info, gpointer **args, guint8 *ret, guint8 *buf, int buf_len)
+{
+	ArchDynCallInfo *dinfo = (ArchDynCallInfo*)info;
+	DynCallArgs *p = (DynCallArgs*)buf;
+	int aindex, arg_index, greg, i, pindex;
+	MonoMethodSignature *sig = dinfo->sig;
+	CallInfo *cinfo = dinfo->cinfo;
+	int buffer_offset = 0;
+
+	g_assert (buf_len >= sizeof (DynCallArgs));
+
+	p->res = 0;
+	p->ret = ret;
+	p->n_fpargs = dinfo->n_fpargs;
+	p->n_fpret = dinfo->n_fpret;
+
+	arg_index = 0;
+	greg = 0;
+	pindex = 0;
+
+	if (sig->hasthis)
+		p->regs [greg ++] = (mgreg_t)*(args [arg_index ++]);
+
+	if (cinfo->ret.storage == ArgVtypeByRef)
+		p->regs [ARMREG_R8] = (mgreg_t)ret;
+
+	for (aindex = pindex; aindex < sig->param_count; aindex++) {
+		MonoType *t = dinfo->param_types [aindex];
+		gpointer *arg = args [arg_index ++];
+		ArgInfo *ainfo = &cinfo->args [aindex + sig->hasthis];
+		int slot = -1;
+
+		if (ainfo->storage == ArgOnStack) {
+			slot = PARAM_REGS + 1 + (ainfo->offset / sizeof (mgreg_t));
+		} else {
+			slot = ainfo->reg;
+		}
+
+		if (t->byref) {
+			p->regs [slot] = (mgreg_t)*arg;
+			continue;
+		}
+
+		if (ios_abi && ainfo->storage == ArgOnStack) {
+			guint8 *stack_arg = (guint8*)&(p->regs [PARAM_REGS + 1]) + ainfo->offset;
+			gboolean handled = TRUE;
+
+			/* Special case arguments smaller than 1 machine word */
+			switch (t->type) {
+			case MONO_TYPE_BOOLEAN:
+			case MONO_TYPE_U1:
+				*(guint8*)stack_arg = *(guint8*)arg;
+				break;
+			case MONO_TYPE_I1:
+				*(gint8*)stack_arg = *(gint8*)arg;
+				break;
+			case MONO_TYPE_U2:
+			case MONO_TYPE_CHAR:
+				*(guint16*)stack_arg = *(guint16*)arg;
+				break;
+			case MONO_TYPE_I2:
+				*(gint16*)stack_arg = *(gint16*)arg;
+				break;
+			case MONO_TYPE_I4:
+				*(gint32*)stack_arg = *(gint32*)arg;
+				break;
+			case MONO_TYPE_U4:
+				*(guint32*)stack_arg = *(guint32*)arg;
+				break;
+			default:
+				handled = FALSE;
+				break;
+			}
+			if (handled)
+				continue;
+		}
+
+		switch (t->type) {
+		case MONO_TYPE_STRING:
+		case MONO_TYPE_CLASS:
+		case MONO_TYPE_ARRAY:
+		case MONO_TYPE_SZARRAY:
+		case MONO_TYPE_OBJECT:
+		case MONO_TYPE_PTR:
+		case MONO_TYPE_I:
+		case MONO_TYPE_U:
+		case MONO_TYPE_I8:
+		case MONO_TYPE_U8:
+			p->regs [slot] = (mgreg_t)*arg;
+			break;
+		case MONO_TYPE_BOOLEAN:
+		case MONO_TYPE_U1:
+			p->regs [slot] = *(guint8*)arg;
+			break;
+		case MONO_TYPE_I1:
+			p->regs [slot] = *(gint8*)arg;
+			break;
+		case MONO_TYPE_I2:
+			p->regs [slot] = *(gint16*)arg;
+			break;
+		case MONO_TYPE_U2:
+		case MONO_TYPE_CHAR:
+			p->regs [slot] = *(guint16*)arg;
+			break;
+		case MONO_TYPE_I4:
+			p->regs [slot] = *(gint32*)arg;
+			break;
+		case MONO_TYPE_U4:
+			p->regs [slot] = *(guint32*)arg;
+			break;
+		case MONO_TYPE_R4:
+			p->fpregs [ainfo->reg] = bitcast_r4_to_r8 (*(float*)arg);
+			p->n_fpargs ++;
+			break;
+		case MONO_TYPE_R8:
+			p->fpregs [ainfo->reg] = *(double*)arg;
+			p->n_fpargs ++;
+			break;
+		case MONO_TYPE_GENERICINST:
+			if (MONO_TYPE_IS_REFERENCE (t)) {
+				p->regs [slot] = (mgreg_t)*arg;
+				break;
+			} else {
+				if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) {
+					MonoClass *klass = mono_class_from_mono_type (t);
+					guint8 *nullable_buf;
+					int size;
+
+					/*
+					 * Use p->buffer as a temporary buffer since the data needs to be available after this call
+					 * if the nullable param is passed by ref.
+					 */
+					size = mono_class_value_size (klass, NULL);
+					nullable_buf = p->buffer + buffer_offset;
+					buffer_offset += size;
+					g_assert (buffer_offset <= 256);
+
+					/* The argument pointed to by arg is either a boxed vtype or null */
+					mono_nullable_init (nullable_buf, (MonoObject*)arg, klass);
+
+					arg = (gpointer*)nullable_buf;
+					/* Fall though */
+				} else {
+					/* Fall though */
+				}
+			}
+		case MONO_TYPE_VALUETYPE:
+			switch (ainfo->storage) {
+			case ArgVtypeInIRegs:
+				for (i = 0; i < ainfo->nregs; ++i)
+					p->regs [slot ++] = ((mgreg_t*)arg) [i];
+				break;
+			case ArgHFA:
+				if (ainfo->esize == 4) {
+					for (i = 0; i < ainfo->nregs; ++i)
+						p->fpregs [ainfo->reg + i] = bitcast_r4_to_r8 (((float*)arg) [ainfo->foffsets [i] / 4]);
+				} else {
+					for (i = 0; i < ainfo->nregs; ++i)
+						p->fpregs [ainfo->reg + i] = ((double*)arg) [ainfo->foffsets [i] / 8];
+				}
+				p->n_fpargs += ainfo->nregs;
+				break;
+			case ArgVtypeByRef:
+				p->regs [slot] = (mgreg_t)arg;
+				break;
+			default:
+				g_assert_not_reached ();
+				break;
+			}
+			break;
+		default:
+			g_assert_not_reached ();
+		}
+	}
+}
+
+void
+mono_arch_finish_dyn_call (MonoDynCallInfo *info, guint8 *buf)
+{
+	ArchDynCallInfo *ainfo = (ArchDynCallInfo*)info;
+	CallInfo *cinfo = ainfo->cinfo;
+	DynCallArgs *args = (DynCallArgs*)buf;
+	MonoType *ptype = ainfo->rtype;
+	guint8 *ret = args->ret;
+	mgreg_t res = args->res;
+	mgreg_t res2 = args->res2;
+	int i;
+
+	if (cinfo->ret.storage == ArgVtypeByRef)
+		return;
+
+	switch (ptype->type) {
+	case MONO_TYPE_VOID:
+		*(gpointer*)ret = NULL;
+		break;
+	case MONO_TYPE_STRING:
+	case MONO_TYPE_CLASS:
+	case MONO_TYPE_ARRAY:
+	case MONO_TYPE_SZARRAY:
+	case MONO_TYPE_OBJECT:
+	case MONO_TYPE_I:
+	case MONO_TYPE_U:
+	case MONO_TYPE_PTR:
+		*(gpointer*)ret = (gpointer)res;
+		break;
+	case MONO_TYPE_I1:
+		*(gint8*)ret = res;
+		break;
+	case MONO_TYPE_U1:
+	case MONO_TYPE_BOOLEAN:
+		*(guint8*)ret = res;
+		break;
+	case MONO_TYPE_I2:
+		*(gint16*)ret = res;
+		break;
+	case MONO_TYPE_U2:
+	case MONO_TYPE_CHAR:
+		*(guint16*)ret = res;
+		break;
+	case MONO_TYPE_I4:
+		*(gint32*)ret = res;
+		break;
+	case MONO_TYPE_U4:
+		*(guint32*)ret = res;
+		break;
+	case MONO_TYPE_I8:
+	case MONO_TYPE_U8:
+		*(guint64*)ret = res;
+		break;
+	case MONO_TYPE_R4:
+		*(float*)ret = bitcast_r8_to_r4 (args->fpregs [0]);
+		break;
+	case MONO_TYPE_R8:
+		*(double*)ret = args->fpregs [0];
+		break;
+	case MONO_TYPE_GENERICINST:
+		if (MONO_TYPE_IS_REFERENCE (ptype)) {
+			*(gpointer*)ret = (gpointer)res;
+			break;
+		} else {
+			/* Fall though */
+		}
+	case MONO_TYPE_VALUETYPE:
+		switch (ainfo->cinfo->ret.storage) {
+		case ArgVtypeInIRegs:
+			*(mgreg_t*)ret = res;
+			if (ainfo->cinfo->ret.nregs > 1)
+				((mgreg_t*)ret) [1] = res2;
+			break;
+		case ArgHFA:
+			/* Use the same area for returning fp values */
+			if (cinfo->ret.esize == 4) {
+				for (i = 0; i < cinfo->ret.nregs; ++i)
+					((float*)ret) [cinfo->ret.foffsets [i] / 4] = bitcast_r8_to_r4 (args->fpregs [i]);
+			} else {
+				for (i = 0; i < cinfo->ret.nregs; ++i)
+					((double*)ret) [cinfo->ret.foffsets [i] / 8] = args->fpregs [i];
+			}
+			break;
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+}
+
+#if __APPLE__
+void sys_icache_invalidate (void *start, size_t len);
+#endif
+
+void
+mono_arch_flush_icache (guint8 *code, gint size)
+{
+#ifndef MONO_CROSS_COMPILE
+#if __APPLE__
+	sys_icache_invalidate (code, size);
+#else
+	__clear_cache (code, code + size);
+#endif
+#endif
+}
+
+#ifndef DISABLE_JIT
+
+gboolean
+mono_arch_opcode_needs_emulation (MonoCompile *cfg, int opcode)
+{
+	NOT_IMPLEMENTED;
+	return FALSE;
+}
+
+GList *
+mono_arch_get_allocatable_int_vars (MonoCompile *cfg)
+{
+	GList *vars = NULL;
+	int i;
+
+	for (i = 0; i < cfg->num_varinfo; i++) {
+		MonoInst *ins = cfg->varinfo [i];
+		MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
+
+		/* unused vars */
+		if (vmv->range.first_use.abs_pos >= vmv->range.last_use.abs_pos)
+			continue;
+
+		if ((ins->flags & (MONO_INST_IS_DEAD|MONO_INST_VOLATILE|MONO_INST_INDIRECT)) || 
+		    (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG))
+			continue;
+
+		if (mono_is_regsize_var (ins->inst_vtype)) {
+			g_assert (MONO_VARINFO (cfg, i)->reg == -1);
+			g_assert (i == vmv->idx);
+			vars = g_list_prepend (vars, vmv);
+		}
+	}
+
+	vars = mono_varlist_sort (cfg, vars, 0);
+
+	return vars;
+}
+
+GList *
+mono_arch_get_global_int_regs (MonoCompile *cfg)
+{
+	GList *regs = NULL;
+	int i;
+
+	/* r28 is reserved for cfg->arch.args_reg */
+	/* r27 is reserved for the imt argument */
+	for (i = ARMREG_R19; i <= ARMREG_R26; ++i)
+		regs = g_list_prepend (regs, GUINT_TO_POINTER (i));
+
+	return regs;
+}
+
+guint32
+mono_arch_regalloc_cost (MonoCompile *cfg, MonoMethodVar *vmv)
+{
+	MonoInst *ins = cfg->varinfo [vmv->idx];
+
+	if (ins->opcode == OP_ARG)
+		return 1;
+	else
+		return 2;
+}
+
+void
+mono_arch_create_vars (MonoCompile *cfg)
+{
+	MonoMethodSignature *sig;
+	CallInfo *cinfo;
+
+	sig = mono_method_signature (cfg->method);
+	if (!cfg->arch.cinfo)
+		cfg->arch.cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig);
+	cinfo = cfg->arch.cinfo;
+
+	if (cinfo->ret.storage == ArgVtypeByRef) {
+		cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+		cfg->vret_addr->flags |= MONO_INST_VOLATILE;
+	}
+
+	if (cfg->gen_sdb_seq_points) {
+		MonoInst *ins;
+
+		if (cfg->compile_aot) {
+			ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+			ins->flags |= MONO_INST_VOLATILE;
+			cfg->arch.seq_point_info_var = ins;
+		}
+
+		ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+		ins->flags |= MONO_INST_VOLATILE;
+		cfg->arch.ss_tramp_var = ins;
+
+		ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+		ins->flags |= MONO_INST_VOLATILE;
+		cfg->arch.bp_tramp_var = ins;
+	}
+
+	if (cfg->method->save_lmf) {
+		cfg->create_lmf_var = TRUE;
+		cfg->lmf_ir = TRUE;
+#ifndef TARGET_MACH
+		cfg->lmf_ir_mono_lmf = TRUE;
+#endif
+	}
+}
+
+void
+mono_arch_allocate_vars (MonoCompile *cfg)
+{
+	MonoMethodSignature *sig;
+	MonoInst *ins;
+	CallInfo *cinfo;
+	ArgInfo *ainfo;
+	int i, offset, size, align;
+	guint32 locals_stack_size, locals_stack_align;
+	gint32 *offsets;
+
+	/*
+	 * Allocate arguments and locals to either register (OP_REGVAR) or to a stack slot (OP_REGOFFSET).
+	 * Compute cfg->stack_offset and update cfg->used_int_regs.
+	 */
+
+	sig = mono_method_signature (cfg->method);
+
+	if (!cfg->arch.cinfo)
+		cfg->arch.cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig);
+	cinfo = cfg->arch.cinfo;
+
+	/*
+	 * The ARM64 ABI always uses a frame pointer.
+	 * The instruction set prefers positive offsets, so fp points to the bottom of the
+	 * frame, and stack slots are at positive offsets.
+	 * If some arguments are received on the stack, their offsets relative to fp can
+	 * not be computed right now because the stack frame might grow due to spilling
+	 * done by the local register allocator. To solve this, we reserve a register
+	 * which points to them.
+	 * The stack frame looks like this:
+	 * args_reg -> <bottom of parent frame>
+	 *             <locals etc>
+	 *       fp -> <saved fp+lr>
+     *       sp -> <localloc/params area>
+	 */
+	cfg->frame_reg = ARMREG_FP;
+	cfg->flags |= MONO_CFG_HAS_SPILLUP;
+	offset = 0;
+
+	/* Saved fp+lr */
+	offset += 16;
+
+	if (cinfo->stack_usage) {
+		g_assert (!(cfg->used_int_regs & (1 << ARMREG_R28)));
+		cfg->arch.args_reg = ARMREG_R28;
+		cfg->used_int_regs |= 1 << ARMREG_R28;
+	}
+
+	if (cfg->method->save_lmf) {
+		/* The LMF var is allocated normally */
+	} else {
+		/* Callee saved regs */
+		cfg->arch.saved_gregs_offset = offset;
+		for (i = 0; i < 32; ++i)
+			if ((MONO_ARCH_CALLEE_SAVED_REGS & (1 << i)) && (cfg->used_int_regs & (1 << i)))
+				offset += 8;
+	}
+
+	/* Return value */
+	switch (cinfo->ret.storage) {
+	case ArgNone:
+		break;
+	case ArgInIReg:
+	case ArgInFReg:
+	case ArgInFRegR4:
+		cfg->ret->opcode = OP_REGVAR;
+		cfg->ret->dreg = cinfo->ret.reg;
+		break;
+	case ArgVtypeInIRegs:
+	case ArgHFA:
+		/* Allocate a local to hold the result, the epilog will copy it to the correct place */
+		cfg->ret->opcode = OP_REGOFFSET;
+		cfg->ret->inst_basereg = cfg->frame_reg;
+		cfg->ret->inst_offset = offset;
+		if (cinfo->ret.storage == ArgHFA)
+			// FIXME:
+			offset += 64;
+		else
+			offset += 16;
+		break;
+	case ArgVtypeByRef:
+		/* This variable will be initalized in the prolog from R8 */
+		cfg->vret_addr->opcode = OP_REGOFFSET;
+		cfg->vret_addr->inst_basereg = cfg->frame_reg;
+		cfg->vret_addr->inst_offset = offset;
+		offset += 8;
+		if (G_UNLIKELY (cfg->verbose_level > 1)) {
+			printf ("vret_addr =");
+			mono_print_ins (cfg->vret_addr);
+		}
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+
+	/* Arguments */
+	for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
+		ainfo = cinfo->args + i;
+
+		ins = cfg->args [i];
+		if (ins->opcode == OP_REGVAR)
+			continue;
+
+		ins->opcode = OP_REGOFFSET;
+		ins->inst_basereg = cfg->frame_reg;
+
+		switch (ainfo->storage) {
+		case ArgInIReg:
+		case ArgInFReg:
+		case ArgInFRegR4:
+			// FIXME: Use nregs/size
+			/* These will be copied to the stack in the prolog */
+			ins->inst_offset = offset;
+			offset += 8;
+			break;
+		case ArgOnStack:
+		case ArgOnStackR4:
+		case ArgOnStackR8:
+		case ArgVtypeOnStack:
+			/* These are in the parent frame */
+			g_assert (cfg->arch.args_reg);
+			ins->inst_basereg = cfg->arch.args_reg;
+			ins->inst_offset = ainfo->offset;
+			break;
+		case ArgVtypeInIRegs:
+		case ArgHFA:
+			ins->opcode = OP_REGOFFSET;
+			ins->inst_basereg = cfg->frame_reg;
+			/* These arguments are saved to the stack in the prolog */
+			ins->inst_offset = offset;
+			if (cfg->verbose_level >= 2)
+				printf ("arg %d allocated to %s+0x%0x.\n", i, mono_arch_regname (ins->inst_basereg), (int)ins->inst_offset);
+			if (ainfo->storage == ArgHFA)
+				// FIXME:
+				offset += 64;
+			else
+				offset += 16;
+			break;
+		case ArgVtypeByRefOnStack: {
+			MonoInst *vtaddr;
+
+			if (ainfo->gsharedvt) {
+				ins->opcode = OP_REGOFFSET;
+				ins->inst_basereg = cfg->arch.args_reg;
+				ins->inst_offset = ainfo->offset;
+				break;
+			}
+
+			/* The vtype address is in the parent frame */
+			g_assert (cfg->arch.args_reg);
+			MONO_INST_NEW (cfg, vtaddr, 0);
+			vtaddr->opcode = OP_REGOFFSET;
+			vtaddr->inst_basereg = cfg->arch.args_reg;
+			vtaddr->inst_offset = ainfo->offset;
+
+			/* Need an indirection */
+			ins->opcode = OP_VTARG_ADDR;
+			ins->inst_left = vtaddr;
+			break;
+		}
+		case ArgVtypeByRef: {
+			MonoInst *vtaddr;
+
+			if (ainfo->gsharedvt) {
+				ins->opcode = OP_REGOFFSET;
+				ins->inst_basereg = cfg->frame_reg;
+				ins->inst_offset = offset;
+				offset += 8;
+				break;
+			}
+
+			/* The vtype address is in a register, will be copied to the stack in the prolog */
+			MONO_INST_NEW (cfg, vtaddr, 0);
+			vtaddr->opcode = OP_REGOFFSET;
+			vtaddr->inst_basereg = cfg->frame_reg;
+			vtaddr->inst_offset = offset;
+			offset += 8;
+
+			/* Need an indirection */
+			ins->opcode = OP_VTARG_ADDR;
+			ins->inst_left = vtaddr;
+			break;
+		}
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+	}
+
+	/* Allocate these first so they have a small offset, OP_SEQ_POINT depends on this */
+	// FIXME: Allocate these to registers
+	ins = cfg->arch.seq_point_info_var;
+	if (ins) {
+		size = 8;
+		align = 8;
+		offset += align - 1;
+		offset &= ~(align - 1);
+		ins->opcode = OP_REGOFFSET;
+		ins->inst_basereg = cfg->frame_reg;
+		ins->inst_offset = offset;
+		offset += size;
+	}
+	ins = cfg->arch.ss_tramp_var;
+	if (ins) {
+		size = 8;
+		align = 8;
+		offset += align - 1;
+		offset &= ~(align - 1);
+		ins->opcode = OP_REGOFFSET;
+		ins->inst_basereg = cfg->frame_reg;
+		ins->inst_offset = offset;
+		offset += size;
+	}
+	ins = cfg->arch.bp_tramp_var;
+	if (ins) {
+		size = 8;
+		align = 8;
+		offset += align - 1;
+		offset &= ~(align - 1);
+		ins->opcode = OP_REGOFFSET;
+		ins->inst_basereg = cfg->frame_reg;
+		ins->inst_offset = offset;
+		offset += size;
+	}
+
+	/* Locals */
+	offsets = mono_allocate_stack_slots (cfg, FALSE, &locals_stack_size, &locals_stack_align);
+	if (locals_stack_align)
+		offset = ALIGN_TO (offset, locals_stack_align);
+
+	for (i = cfg->locals_start; i < cfg->num_varinfo; i++) {
+		if (offsets [i] != -1) {
+			ins = cfg->varinfo [i];
+			ins->opcode = OP_REGOFFSET;
+			ins->inst_basereg = cfg->frame_reg;
+			ins->inst_offset = offset + offsets [i];
+			//printf ("allocated local %d to ", i); mono_print_tree_nl (ins);
+		}
+	}
+	offset += locals_stack_size;
+
+	offset = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
+
+	cfg->stack_offset = offset;
+}
+
+#ifdef ENABLE_LLVM
+LLVMCallInfo*
+mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
+{
+	int i, n;
+	CallInfo *cinfo;
+	ArgInfo *ainfo;
+	LLVMCallInfo *linfo;
+
+	n = sig->param_count + sig->hasthis;
+
+	cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig);
+
+	linfo = mono_mempool_alloc0 (cfg->mempool, sizeof (LLVMCallInfo) + (sizeof (LLVMArgInfo) * n));
+
+	switch (cinfo->ret.storage) {
+	case ArgInIReg:
+	case ArgInFReg:
+	case ArgInFRegR4:
+	case ArgNone:
+		break;
+	case ArgVtypeByRef:
+		linfo->ret.storage = LLVMArgVtypeByRef;
+		break;
+		//
+		// FIXME: This doesn't work yet since the llvm backend represents these types as an i8
+		// array which is returned in int regs
+		//
+	case ArgHFA:
+		linfo->ret.storage = LLVMArgFpStruct;
+		linfo->ret.nslots = cinfo->ret.nregs;
+		linfo->ret.esize = cinfo->ret.esize;
+		break;
+	case ArgVtypeInIRegs:
+		/* LLVM models this by returning an int */
+		linfo->ret.storage = LLVMArgVtypeAsScalar;
+		linfo->ret.nslots = cinfo->ret.nregs;
+		linfo->ret.esize = cinfo->ret.esize;
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+
+	for (i = 0; i < n; ++i) {
+		LLVMArgInfo *lainfo = &linfo->args [i];
+
+		ainfo = cinfo->args + i;
+
+		lainfo->storage = LLVMArgNone;
+
+		switch (ainfo->storage) {
+		case ArgInIReg:
+		case ArgInFReg:
+		case ArgInFRegR4:
+		case ArgOnStack:
+		case ArgOnStackR4:
+		case ArgOnStackR8:
+			lainfo->storage = LLVMArgInIReg;
+			break;
+		case ArgVtypeByRef:
+		case ArgVtypeByRefOnStack:
+			lainfo->storage = LLVMArgVtypeByRef;
+			break;
+		case ArgHFA: {
+			int j;
+
+			lainfo->storage = LLVMArgAsFpArgs;
+			lainfo->nslots = ainfo->nregs;
+			lainfo->esize = ainfo->esize;
+			for (j = 0; j < ainfo->nregs; ++j)
+				lainfo->pair_storage [j] = LLVMArgInFPReg;
+			break;
+		}
+		case ArgVtypeInIRegs:
+			lainfo->storage = LLVMArgAsIArgs;
+			lainfo->nslots = ainfo->nregs;
+			break;
+		case ArgVtypeOnStack:
+			if (ainfo->hfa) {
+				int j;
+				/* Same as above */
+				lainfo->storage = LLVMArgAsFpArgs;
+				lainfo->nslots = ainfo->nregs;
+				lainfo->esize = ainfo->esize;
+				for (j = 0; j < ainfo->nregs; ++j)
+					lainfo->pair_storage [j] = LLVMArgInFPReg;
+			} else {
+				lainfo->storage = LLVMArgAsIArgs;
+				lainfo->nslots = ainfo->size / 8;
+			}
+			break;
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+	}
+
+	return linfo;
+}
+#endif
+
+static void
+add_outarg_reg (MonoCompile *cfg, MonoCallInst *call, ArgStorage storage, int reg, MonoInst *arg)
+{
+	MonoInst *ins;
+
+	switch (storage) {
+	case ArgInIReg:
+		MONO_INST_NEW (cfg, ins, OP_MOVE);
+		ins->dreg = mono_alloc_ireg_copy (cfg, arg->dreg);
+		ins->sreg1 = arg->dreg;
+		MONO_ADD_INS (cfg->cbb, ins);
+		mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, FALSE);
+		break;
+	case ArgInFReg:
+		MONO_INST_NEW (cfg, ins, OP_FMOVE);
+		ins->dreg = mono_alloc_freg (cfg);
+		ins->sreg1 = arg->dreg;
+		MONO_ADD_INS (cfg->cbb, ins);
+		mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, TRUE);
+		break;
+	case ArgInFRegR4:
+		if (COMPILE_LLVM (cfg))
+			MONO_INST_NEW (cfg, ins, OP_FMOVE);
+		else if (cfg->r4fp)
+			MONO_INST_NEW (cfg, ins, OP_RMOVE);
+		else
+			MONO_INST_NEW (cfg, ins, OP_ARM_SETFREG_R4);
+		ins->dreg = mono_alloc_freg (cfg);
+		ins->sreg1 = arg->dreg;
+		MONO_ADD_INS (cfg->cbb, ins);
+		mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, TRUE);
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+static void
+emit_sig_cookie (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo)
+{
+	MonoMethodSignature *tmp_sig;
+	int sig_reg;
+
+	if (call->tail_call)
+		NOT_IMPLEMENTED;
+
+	g_assert (cinfo->sig_cookie.storage == ArgOnStack);
+			
+	/*
+	 * mono_ArgIterator_Setup assumes the signature cookie is 
+	 * passed first and all the arguments which were before it are
+	 * passed on the stack after the signature. So compensate by 
+	 * passing a different signature.
+	 */
+	tmp_sig = mono_metadata_signature_dup (call->signature);
+	tmp_sig->param_count -= call->signature->sentinelpos;
+	tmp_sig->sentinelpos = 0;
+	memcpy (tmp_sig->params, call->signature->params + call->signature->sentinelpos, tmp_sig->param_count * sizeof (MonoType*));
+
+	sig_reg = mono_alloc_ireg (cfg);
+	MONO_EMIT_NEW_SIGNATURECONST (cfg, sig_reg, tmp_sig);
+
+	MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, cinfo->sig_cookie.offset, sig_reg);
+}
+
+void
+mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call)
+{
+	MonoMethodSignature *sig;
+	MonoInst *arg, *vtarg;
+	CallInfo *cinfo;
+	ArgInfo *ainfo;
+	int i;
+
+	sig = call->signature;
+
+	cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig);
+
+	switch (cinfo->ret.storage) {
+	case ArgVtypeInIRegs:
+	case ArgHFA:
+		/*
+		 * The vtype is returned in registers, save the return area address in a local, and save the vtype into
+		 * the location pointed to by it after call in emit_move_return_value ().
+		 */
+		if (!cfg->arch.vret_addr_loc) {
+			cfg->arch.vret_addr_loc = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+			/* Prevent it from being register allocated or optimized away */
+			((MonoInst*)cfg->arch.vret_addr_loc)->flags |= MONO_INST_VOLATILE;
+		}
+
+		MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ((MonoInst*)cfg->arch.vret_addr_loc)->dreg, call->vret_var->dreg);
+		break;
+	case ArgVtypeByRef:
+		/* Pass the vtype return address in R8 */
+		MONO_INST_NEW (cfg, vtarg, OP_MOVE);
+		vtarg->sreg1 = call->vret_var->dreg;
+		vtarg->dreg = mono_alloc_preg (cfg);
+		MONO_ADD_INS (cfg->cbb, vtarg);
+
+		mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
+		break;
+	default:
+		break;
+	}
+
+	for (i = 0; i < cinfo->nargs; ++i) {
+		ainfo = cinfo->args + i;
+		arg = call->args [i];
+
+		if ((sig->call_convention == MONO_CALL_VARARG) && (i == sig->sentinelpos)) {
+			/* Emit the signature cookie just before the implicit arguments */
+			emit_sig_cookie (cfg, call, cinfo);
+		}
+
+		switch (ainfo->storage) {
+		case ArgInIReg:
+		case ArgInFReg:
+		case ArgInFRegR4:
+			add_outarg_reg (cfg, call, ainfo->storage, ainfo->reg, arg);
+			break;
+		case ArgOnStack:
+			switch (ainfo->slot_size) {
+			case 8:
+				MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
+				break;
+			case 4:
+				MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
+				break;
+			case 2:
+				MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
+				break;
+			case 1:
+				MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
+				break;
+			default:
+				g_assert_not_reached ();
+				break;
+			}
+			break;
+		case ArgOnStackR8:
+			MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
+			break;
+		case ArgOnStackR4:
+			MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, ARMREG_SP, ainfo->offset, arg->dreg);
+			break;
+		case ArgVtypeInIRegs:
+		case ArgVtypeByRef:
+		case ArgVtypeByRefOnStack:
+		case ArgVtypeOnStack:
+		case ArgHFA: {
+			MonoInst *ins;
+			guint32 align;
+			guint32 size;
+
+			size = mono_class_value_size (arg->klass, &align);
+
+			MONO_INST_NEW (cfg, ins, OP_OUTARG_VT);
+			ins->sreg1 = arg->dreg;
+			ins->klass = arg->klass;
+			ins->backend.size = size;
+			ins->inst_p0 = call;
+			ins->inst_p1 = mono_mempool_alloc (cfg->mempool, sizeof (ArgInfo));
+			memcpy (ins->inst_p1, ainfo, sizeof (ArgInfo));
+			MONO_ADD_INS (cfg->cbb, ins);
+			break;
+		}
+		default:
+			g_assert_not_reached ();
+			break;
+		}
+	}
+
+	/* Handle the case where there are no implicit arguments */
+	if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (cinfo->nargs == sig->sentinelpos))
+		emit_sig_cookie (cfg, call, cinfo);
+
+	call->call_info = cinfo;
+	call->stack_usage = cinfo->stack_usage;
+}
+
+void
+mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src)
+{
+	MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
+	ArgInfo *ainfo = ins->inst_p1;
+	MonoInst *load;
+	int i;
+
+	if (ins->backend.size == 0 && !ainfo->gsharedvt)
+		return;
+
+	switch (ainfo->storage) {
+	case ArgVtypeInIRegs:
+		for (i = 0; i < ainfo->nregs; ++i) {
+			// FIXME: Smaller sizes
+			MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
+			load->dreg = mono_alloc_ireg (cfg);
+			load->inst_basereg = src->dreg;
+			load->inst_offset = i * sizeof(mgreg_t);
+			MONO_ADD_INS (cfg->cbb, load);
+			add_outarg_reg (cfg, call, ArgInIReg, ainfo->reg + i, load);
+		}
+		break;
+	case ArgHFA:
+		for (i = 0; i < ainfo->nregs; ++i) {
+			if (ainfo->esize == 4)
+				MONO_INST_NEW (cfg, load, OP_LOADR4_MEMBASE);
+			else
+				MONO_INST_NEW (cfg, load, OP_LOADR8_MEMBASE);
+			load->dreg = mono_alloc_freg (cfg);
+			load->inst_basereg = src->dreg;
+			load->inst_offset = ainfo->foffsets [i];
+			MONO_ADD_INS (cfg->cbb, load);
+			add_outarg_reg (cfg, call, ainfo->esize == 4 ? ArgInFRegR4 : ArgInFReg, ainfo->reg + i, load);
+		}
+		break;
+	case ArgVtypeByRef:
+	case ArgVtypeByRefOnStack: {
+		MonoInst *vtaddr, *load, *arg;
+
+		/* Pass the vtype address in a reg/on the stack */
+		if (ainfo->gsharedvt) {
+			load = src;
+		} else {
+			/* Make a copy of the argument */
+			vtaddr = mono_compile_create_var (cfg, &ins->klass->byval_arg, OP_LOCAL);
+
+			MONO_INST_NEW (cfg, load, OP_LDADDR);
+			load->inst_p0 = vtaddr;
+			vtaddr->flags |= MONO_INST_INDIRECT;
+			load->type = STACK_MP;
+			load->klass = vtaddr->klass;
+			load->dreg = mono_alloc_ireg (cfg);
+			MONO_ADD_INS (cfg->cbb, load);
+			mini_emit_memcpy (cfg, load->dreg, 0, src->dreg, 0, ainfo->size, 8);
+		}
+
+		if (ainfo->storage == ArgVtypeByRef) {
+			MONO_INST_NEW (cfg, arg, OP_MOVE);
+			arg->dreg = mono_alloc_preg (cfg);
+			arg->sreg1 = load->dreg;
+			MONO_ADD_INS (cfg->cbb, arg);
+			add_outarg_reg (cfg, call, ArgInIReg, ainfo->reg, arg);
+		} else {
+			MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, ARMREG_SP, ainfo->offset, load->dreg);
+		}
+		break;
+	}
+	case ArgVtypeOnStack:
+		for (i = 0; i < ainfo->size / 8; ++i) {
+			MONO_INST_NEW (cfg, load, OP_LOADI8_MEMBASE);
+			load->dreg = mono_alloc_ireg (cfg);
+			load->inst_basereg = src->dreg;
+			load->inst_offset = i * 8;
+			MONO_ADD_INS (cfg->cbb, load);
+			MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, ARMREG_SP, ainfo->offset + (i * 8), load->dreg);
+		}
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+void
+mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val)
+{
+	MonoMethodSignature *sig;
+	CallInfo *cinfo;
+
+	sig = mono_method_signature (cfg->method);
+	if (!cfg->arch.cinfo)
+		cfg->arch.cinfo = get_call_info (cfg->generic_sharing_context, cfg->mempool, sig);
+	cinfo = cfg->arch.cinfo;
+
+	switch (cinfo->ret.storage) {
+	case ArgNone:
+		break;
+	case ArgInIReg:
+		MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->ret->dreg, val->dreg);
+		break;
+	case ArgInFReg:
+		MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
+		break;
+	case ArgInFRegR4:
+		if (COMPILE_LLVM (cfg))
+			MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, cfg->ret->dreg, val->dreg);
+		else if (cfg->r4fp)
+			MONO_EMIT_NEW_UNALU (cfg, OP_RMOVE, cfg->ret->dreg, val->dreg);
+		else
+			MONO_EMIT_NEW_UNALU (cfg, OP_ARM_SETFREG_R4, cfg->ret->dreg, val->dreg);
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+gboolean
+mono_arch_tail_call_supported (MonoCompile *cfg, MonoMethodSignature *caller_sig, MonoMethodSignature *callee_sig)
+{
+	CallInfo *c1, *c2;
+	gboolean res;
+
+	if (cfg->compile_aot && !cfg->full_aot)
+		/* OP_TAILCALL doesn't work with AOT */
+		return FALSE;
+
+	c1 = get_call_info (NULL, NULL, caller_sig);
+	c2 = get_call_info (NULL, NULL, callee_sig);
+	res = TRUE;
+	// FIXME: Relax these restrictions
+	if (c1->stack_usage != 0)
+		res = FALSE;
+	if (c1->stack_usage != c2->stack_usage)
+		res = FALSE;
+	if ((c1->ret.storage != ArgNone && c1->ret.storage != ArgInIReg) || c1->ret.storage != c2->ret.storage)
+		res = FALSE;
+
+	g_free (c1);
+	g_free (c2);
+
+	return res;
+}
+
+gboolean 
+mono_arch_is_inst_imm (gint64 imm)
+{
+	return (imm >= -((gint64)1<<31) && imm <= (((gint64)1<<31)-1));
+}
+
+void*
+mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments)
+{
+	NOT_IMPLEMENTED;
+	return NULL;
+}
+
+void*
+mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments, gboolean preserve_argument_registers)
+{
+	NOT_IMPLEMENTED;
+	return NULL;
+}
+
+void
+mono_arch_peephole_pass_1 (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+	//NOT_IMPLEMENTED;
+}
+
+void
+mono_arch_peephole_pass_2 (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+	//NOT_IMPLEMENTED;
+}
+
+#define ADD_NEW_INS(cfg,dest,op) do {       \
+		MONO_INST_NEW ((cfg), (dest), (op)); \
+        mono_bblock_insert_before_ins (bb, ins, (dest)); \
+	} while (0)
+
+void
+mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+	MonoInst *ins, *temp, *last_ins = NULL;
+
+	MONO_BB_FOR_EACH_INS (bb, ins) {
+		switch (ins->opcode) {
+		case OP_SBB:
+		case OP_ISBB:
+		case OP_SUBCC:
+		case OP_ISUBCC:
+			if (ins->next  && (ins->next->opcode == OP_COND_EXC_C || ins->next->opcode == OP_COND_EXC_IC))
+				/* ARM sets the C flag to 1 if there was _no_ overflow */
+				ins->next->opcode = OP_COND_EXC_NC;
+			break;
+		case OP_IDIV_IMM:
+		case OP_IREM_IMM:
+		case OP_IDIV_UN_IMM:
+		case OP_IREM_UN_IMM:
+		case OP_LREM_IMM:
+			mono_decompose_op_imm (cfg, bb, ins);
+			break;
+		case OP_LOCALLOC_IMM:
+			if (ins->inst_imm > 32) {
+				ADD_NEW_INS (cfg, temp, OP_ICONST);
+				temp->inst_c0 = ins->inst_imm;
+				temp->dreg = mono_alloc_ireg (cfg);
+				ins->sreg1 = temp->dreg;
+				ins->opcode = mono_op_imm_to_op (ins->opcode);
+			}
+			break;
+		case OP_ICOMPARE_IMM:
+			if (ins->inst_imm == 0 && ins->next && ins->next->opcode == OP_IBEQ) {
+				ins->next->opcode = OP_ARM64_CBZW;
+				ins->next->sreg1 = ins->sreg1;
+				NULLIFY_INS (ins);
+			} else if (ins->inst_imm == 0 && ins->next && ins->next->opcode == OP_IBNE_UN) {
+				ins->next->opcode = OP_ARM64_CBNZW;
+				ins->next->sreg1 = ins->sreg1;
+				NULLIFY_INS (ins);
+			}
+			break;
+		case OP_LCOMPARE_IMM:
+		case OP_COMPARE_IMM:
+			if (ins->inst_imm == 0 && ins->next && ins->next->opcode == OP_LBEQ) {
+				ins->next->opcode = OP_ARM64_CBZX;
+				ins->next->sreg1 = ins->sreg1;
+				NULLIFY_INS (ins);
+			} else if (ins->inst_imm == 0 && ins->next && ins->next->opcode == OP_LBNE_UN) {
+				ins->next->opcode = OP_ARM64_CBNZX;
+				ins->next->sreg1 = ins->sreg1;
+				NULLIFY_INS (ins);
+			}
+			break;
+		case OP_FCOMPARE: {
+			gboolean swap = FALSE;
+			int reg;
+
+			if (!ins->next) {
+				/* Optimized away */
+				NULLIFY_INS (ins);
+				break;
+			}
+
+			/*
+			 * FP compares with unordered operands set the flags
+			 * to NZCV=0011, which matches some non-unordered compares
+			 * as well, like LE, so have to swap the operands.
+			 */
+			switch (ins->next->opcode) {
+			case OP_FBLT:
+				ins->next->opcode = OP_FBGT;
+				swap = TRUE;
+				break;
+			case OP_FBLE:
+				ins->next->opcode = OP_FBGE;
+				swap = TRUE;
+				break;
+			default:
+				break;
+			}
+			if (swap) {
+				reg = ins->sreg1;
+				ins->sreg1 = ins->sreg2;
+				ins->sreg2 = reg;
+			}
+			break;
+		}
+		default:
+			break;
+		}
+
+		last_ins = ins;
+	}
+	bb->last_ins = last_ins;
+	bb->max_vreg = cfg->next_vreg;
+}
+
+void
+mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
+{
+}
+
+static int
+opcode_to_armcond (int opcode)
+{
+	switch (opcode) {
+	case OP_IBEQ:
+	case OP_LBEQ:
+	case OP_FBEQ:
+	case OP_CEQ:
+	case OP_ICEQ:
+	case OP_LCEQ:
+	case OP_FCEQ:
+	case OP_RCEQ:
+	case OP_COND_EXC_IEQ:
+	case OP_COND_EXC_EQ:
+		return ARMCOND_EQ;
+	case OP_IBGE:
+	case OP_LBGE:
+	case OP_FBGE:
+	case OP_ICGE:
+	case OP_FCGE:
+	case OP_RCGE:
+		return ARMCOND_GE;
+	case OP_IBGT:
+	case OP_LBGT:
+	case OP_FBGT:
+	case OP_CGT:
+	case OP_ICGT:
+	case OP_LCGT:
+	case OP_FCGT:
+	case OP_RCGT:
+	case OP_COND_EXC_IGT:
+	case OP_COND_EXC_GT:
+		return ARMCOND_GT;
+	case OP_IBLE:
+	case OP_LBLE:
+	case OP_FBLE:
+	case OP_ICLE:
+	case OP_FCLE:
+	case OP_RCLE:
+		return ARMCOND_LE;
+	case OP_IBLT:
+	case OP_LBLT:
+	case OP_FBLT:
+	case OP_CLT:
+	case OP_ICLT:
+	case OP_LCLT:
+	case OP_COND_EXC_ILT:
+	case OP_COND_EXC_LT:
+		return ARMCOND_LT;
+	case OP_IBNE_UN:
+	case OP_LBNE_UN:
+	case OP_FBNE_UN:
+	case OP_ICNEQ:
+	case OP_FCNEQ:
+	case OP_RCNEQ:
+	case OP_COND_EXC_INE_UN:
+	case OP_COND_EXC_NE_UN:
+		return ARMCOND_NE;
+	case OP_IBGE_UN:
+	case OP_LBGE_UN:
+	case OP_FBGE_UN:
+	case OP_ICGE_UN:
+	case OP_COND_EXC_IGE_UN:
+	case OP_COND_EXC_GE_UN:
+		return ARMCOND_HS;
+	case OP_IBGT_UN:
+	case OP_LBGT_UN:
+	case OP_FBGT_UN:
+	case OP_CGT_UN:
+	case OP_ICGT_UN:
+	case OP_LCGT_UN:
+	case OP_FCGT_UN:
+	case OP_RCGT_UN:
+	case OP_COND_EXC_IGT_UN:
+	case OP_COND_EXC_GT_UN:
+		return ARMCOND_HI;
+	case OP_IBLE_UN:
+	case OP_LBLE_UN:
+	case OP_FBLE_UN:
+	case OP_ICLE_UN:
+	case OP_COND_EXC_ILE_UN:
+	case OP_COND_EXC_LE_UN:
+		return ARMCOND_LS;
+	case OP_IBLT_UN:
+	case OP_LBLT_UN:
+	case OP_FBLT_UN:
+	case OP_CLT_UN:
+	case OP_ICLT_UN:
+	case OP_LCLT_UN:
+	case OP_COND_EXC_ILT_UN:
+	case OP_COND_EXC_LT_UN:
+		return ARMCOND_LO;
+		/*
+		 * FCMP sets the NZCV condition bits as follows:
+		 * eq = 0110
+		 * < = 1000
+		 * > = 0010
+		 * unordered = 0011
+		 * ARMCOND_LT is N!=V, so it matches unordered too, so
+		 * fclt and fclt_un need to be special cased.
+		 */
+	case OP_FCLT:
+	case OP_RCLT:
+		/* N==1 */
+		return ARMCOND_MI;
+	case OP_FCLT_UN:
+	case OP_RCLT_UN:
+		return ARMCOND_LT;
+	case OP_COND_EXC_C:
+	case OP_COND_EXC_IC:
+		return ARMCOND_CS;
+	case OP_COND_EXC_OV:
+	case OP_COND_EXC_IOV:
+		return ARMCOND_VS;
+	case OP_COND_EXC_NC:
+	case OP_COND_EXC_INC:
+		return ARMCOND_CC;
+	case OP_COND_EXC_NO:
+	case OP_COND_EXC_INO:
+		return ARMCOND_VC;
+	default:
+		printf ("%s\n", mono_inst_name (opcode));
+		g_assert_not_reached ();
+		return -1;
+	}
+}
+
+/* This clobbers LR */
+static inline __attribute__((warn_unused_result)) guint8*
+emit_cond_exc (MonoCompile *cfg, guint8 *code, int opcode, const char *exc_name)
+{
+	int cond;
+
+	cond = opcode_to_armcond (opcode);
+	/* Capture PC */
+	arm_adrx (code, ARMREG_IP1, code);
+	mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_EXC, exc_name, MONO_R_ARM64_BCC);
+	arm_bcc (code, cond, 0);
+	return code;
+}
+
+static guint8*
+emit_move_return_value (MonoCompile *cfg, guint8 * code, MonoInst *ins)
+{
+	CallInfo *cinfo;
+	MonoCallInst *call;
+
+	call = (MonoCallInst*)ins;
+	cinfo = call->call_info;
+	g_assert (cinfo);
+	switch (cinfo->ret.storage) {
+	case ArgNone:
+		break;
+	case ArgInIReg:
+		/* LLVM compiled code might only set the bottom bits */
+		if (call->signature && mini_get_underlying_type (cfg, call->signature->ret)->type == MONO_TYPE_I4)
+			arm_sxtwx (code, call->inst.dreg, cinfo->ret.reg);
+		else if (call->inst.dreg != cinfo->ret.reg)
+			arm_movx (code, call->inst.dreg, cinfo->ret.reg);
+		break;
+	case ArgInFReg:
+		if (call->inst.dreg != cinfo->ret.reg)
+			arm_fmovd (code, call->inst.dreg, cinfo->ret.reg);
+		break;
+	case ArgInFRegR4:
+		if (cfg->r4fp)
+			arm_fmovs (code, call->inst.dreg, cinfo->ret.reg);
+		else
+			arm_fcvt_sd (code, call->inst.dreg, cinfo->ret.reg);
+		break;
+	case ArgVtypeInIRegs: {
+		MonoInst *loc = cfg->arch.vret_addr_loc;
+		int i;
+
+		/* Load the destination address */
+		g_assert (loc && loc->opcode == OP_REGOFFSET);
+		code = emit_ldrx (code, ARMREG_LR, loc->inst_basereg, loc->inst_offset);
+		for (i = 0; i < cinfo->ret.nregs; ++i)
+			arm_strx (code, cinfo->ret.reg + i, ARMREG_LR, i * 8);
+		break;
+	}
+	case ArgHFA: {
+		MonoInst *loc = cfg->arch.vret_addr_loc;
+		int i;
+
+		/* Load the destination address */
+		g_assert (loc && loc->opcode == OP_REGOFFSET);
+		code = emit_ldrx (code, ARMREG_LR, loc->inst_basereg, loc->inst_offset);
+		for (i = 0; i < cinfo->ret.nregs; ++i) {
+			if (cinfo->ret.esize == 4)
+				arm_strfpw (code, cinfo->ret.reg + i, ARMREG_LR, cinfo->ret.foffsets [i]);
+			else
+				arm_strfpx (code, cinfo->ret.reg + i, ARMREG_LR, cinfo->ret.foffsets [i]);
+		}
+		break;
+	}
+	case ArgVtypeByRef:
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+	return code;
+}
+
+/*
+ * emit_branch_island:
+ *
+ *   Emit a branch island for the conditional branches from cfg->native_code + start_offset to code.
+ */
+static guint8*
+emit_branch_island (MonoCompile *cfg, guint8 *code, int start_offset)
+{
+	MonoJumpInfo *ji;
+	int offset, island_size;
+
+	/* Iterate over the patch infos added so far by this bb */
+	island_size = 0;
+	for (ji = cfg->patch_info; ji; ji = ji->next) {
+		if (ji->ip.i < start_offset)
+			/* The patch infos are in reverse order, so this means the end */
+			break;
+		if (ji->relocation == MONO_R_ARM64_BCC || ji->relocation == MONO_R_ARM64_CBZ)
+			island_size += 4;
+	}
+
+	if (island_size) {
+		offset = code - cfg->native_code;
+		if (offset > (cfg->code_size - island_size - 16)) {
+			cfg->code_size *= 2;
+			cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
+			code = cfg->native_code + offset;
+		}
+
+		/* Branch over the island */
+		arm_b (code, code + 4 + island_size);
+
+		for (ji = cfg->patch_info; ji; ji = ji->next) {
+			if (ji->ip.i < start_offset)
+				break;
+			if (ji->relocation == MONO_R_ARM64_BCC || ji->relocation == MONO_R_ARM64_CBZ) {
+				/* Rewrite the cond branch so it branches to an uncoditional branch in the branch island */
+				arm_patch_rel (cfg->native_code + ji->ip.i, code, ji->relocation);
+				/* Rewrite the patch so it points to the unconditional branch */
+				ji->ip.i = code - cfg->native_code;
+				ji->relocation = MONO_R_ARM64_B;
+				arm_b (code, code);
+			}
+		}
+	}
+	return code;
+}
+
+void
+mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+	MonoInst *ins;
+	MonoCallInst *call;
+	guint offset;
+	guint8 *code = cfg->native_code + cfg->code_len;
+	int start_offset, max_len, dreg, sreg1, sreg2;
+	mgreg_t imm;
+
+	if (cfg->verbose_level > 2)
+		g_print ("Basic block %d starting at offset 0x%x\n", bb->block_num, bb->native_offset);
+
+	start_offset = code - cfg->native_code;
+
+	MONO_BB_FOR_EACH_INS (bb, ins) {
+		offset = code - cfg->native_code;
+
+		max_len = ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
+
+		if (offset > (cfg->code_size - max_len - 16)) {
+			cfg->code_size *= 2;
+			cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
+			code = cfg->native_code + offset;
+		}
+
+		if (G_UNLIKELY (cfg->arch.cond_branch_islands && offset - start_offset > 4 * 0x1ffff)) {
+			/* Emit a branch island for large basic blocks */
+			code = emit_branch_island (cfg, code, start_offset);
+			offset = code - cfg->native_code;
+			start_offset = offset;
+		}
+
+		mono_debug_record_line_number (cfg, ins, offset);
+
+		dreg = ins->dreg;
+		sreg1 = ins->sreg1;
+		sreg2 = ins->sreg2;
+		imm = ins->inst_imm;
+
+		switch (ins->opcode) {
+		case OP_ICONST:
+			code = emit_imm (code, dreg, ins->inst_c0);
+			break;
+		case OP_I8CONST:
+			code = emit_imm64 (code, dreg, ins->inst_c0);
+			break;
+		case OP_MOVE:
+			if (dreg != sreg1)
+				arm_movx (code, dreg, sreg1);
+			break;
+		case OP_NOP:
+		case OP_RELAXED_NOP:
+			break;
+		case OP_JUMP_TABLE:
+			mono_add_patch_info_rel (cfg, offset, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0, MONO_R_ARM64_IMM);
+			code = emit_imm64_template (code, dreg);
+			break;
+		case OP_BREAK:
+			/*
+			 * gdb does not like encountering the hw breakpoint ins in the debugged code. 
+			 * So instead of emitting a trap, we emit a call a C function and place a 
+			 * breakpoint there.
+			 */
+			code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, (gpointer)"mono_break");
+			break;
+		case OP_LOCALLOC: {
+			guint8 *buf [16];
+
+			arm_addx_imm (code, ARMREG_IP0, sreg1, (MONO_ARCH_FRAME_ALIGNMENT - 1));
+			// FIXME: andx_imm doesn't work yet
+			code = emit_imm (code, ARMREG_IP1, -MONO_ARCH_FRAME_ALIGNMENT);
+			arm_andx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
+			//arm_andx_imm (code, ARMREG_IP0, sreg1, - MONO_ARCH_FRAME_ALIGNMENT);
+			arm_movspx (code, ARMREG_IP1, ARMREG_SP);
+			arm_subx (code, ARMREG_IP1, ARMREG_IP1, ARMREG_IP0);
+			arm_movspx (code, ARMREG_SP, ARMREG_IP1);
+
+			/* Init */
+			/* ip1 = pointer, ip0 = end */
+			arm_addx (code, ARMREG_IP0, ARMREG_IP1, ARMREG_IP0);
+			buf [0] = code;
+			arm_cmpx (code, ARMREG_IP1, ARMREG_IP0);
+			buf [1] = code;
+			arm_bcc (code, ARMCOND_EQ, 0);
+			arm_stpx (code, ARMREG_RZR, ARMREG_RZR, ARMREG_IP1, 0);
+			arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 16);
+			arm_b (code, buf [0]);
+			arm_patch_rel (buf [1], code, MONO_R_ARM64_BCC);
+
+			arm_movspx (code, dreg, ARMREG_SP);
+			if (cfg->param_area)
+				code = emit_subx_sp_imm (code, cfg->param_area);
+			break;
+		}
+		case OP_LOCALLOC_IMM: {
+			int imm, offset;
+
+			imm = ALIGN_TO (ins->inst_imm, MONO_ARCH_FRAME_ALIGNMENT);
+			g_assert (arm_is_arith_imm (imm));
+			arm_subx_imm (code, ARMREG_SP, ARMREG_SP, imm);
+
+			/* Init */
+			g_assert (MONO_ARCH_FRAME_ALIGNMENT == 16);
+			offset = 0;
+			while (offset < imm) {
+				arm_stpx (code, ARMREG_RZR, ARMREG_RZR, ARMREG_SP, offset);
+				offset += 16;
+			}
+			arm_movspx (code, dreg, ARMREG_SP);
+			if (cfg->param_area)
+				code = emit_subx_sp_imm (code, cfg->param_area);
+			break;
+		}
+		case OP_AOTCONST:
+			code = emit_aotconst (cfg, code, dreg, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
+			break;
+		case OP_OBJC_GET_SELECTOR:
+			mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_OBJC_SELECTOR_REF, ins->inst_p0);
+			/* See arch_emit_objc_selector_ref () in aot-compiler.c */
+			arm_ldrx_lit (code, ins->dreg, 0);
+			arm_nop (code);
+			arm_nop (code);
+			break;
+		case OP_SEQ_POINT: {
+			MonoInst *info_var = cfg->arch.seq_point_info_var;
+
+			/*
+			 * For AOT, we use one got slot per method, which will point to a
+			 * SeqPointInfo structure, containing all the information required
+			 * by the code below.
+			 */
+			if (cfg->compile_aot) {
+				g_assert (info_var);
+				g_assert (info_var->opcode == OP_REGOFFSET);
+			}
+
+			if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
+				MonoInst *var = cfg->arch.ss_tramp_var;
+
+				g_assert (var);
+				g_assert (var->opcode == OP_REGOFFSET);
+				/* Load ss_tramp_var */
+				/* This is equal to &ss_trampoline */
+				arm_ldrx (code, ARMREG_IP1, var->inst_basereg, var->inst_offset);
+				/* Load the trampoline address */
+				arm_ldrx (code, ARMREG_IP1, ARMREG_IP1, 0);
+				/* Call it if it is non-null */
+				arm_cbzx (code, ARMREG_IP1, code + 8);
+				arm_blrx (code, ARMREG_IP1);
+			}
+
+			mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+
+			if (cfg->compile_aot) {
+				guint32 offset = code - cfg->native_code;
+				guint32 val;
+
+				arm_ldrx (code, ARMREG_IP1, info_var->inst_basereg, info_var->inst_offset);
+				/* Add the offset */
+				val = ((offset / 4) * sizeof (guint8*)) + MONO_STRUCT_OFFSET (SeqPointInfo, bp_addrs);
+				/* Load the info->bp_addrs [offset], which is either 0 or the address of the bp trampoline */
+				code = emit_ldrx (code, ARMREG_IP1, ARMREG_IP1, val);
+				/* Skip the load if its 0 */
+				arm_cbzx (code, ARMREG_IP1, code + 8);
+				/* Call the breakpoint trampoline */
+				arm_blrx (code, ARMREG_IP1);
+			} else {
+				MonoInst *var = cfg->arch.bp_tramp_var;
+
+				g_assert (var);
+				g_assert (var->opcode == OP_REGOFFSET);
+				/* Load the address of the bp trampoline into IP0 */
+				arm_ldrx (code, ARMREG_IP0, var->inst_basereg, var->inst_offset);
+				/* 
+				 * A placeholder for a possible breakpoint inserted by
+				 * mono_arch_set_breakpoint ().
+				 */
+				arm_nop (code);
+			}
+			break;
+		}
+
+			/* BRANCH */
+		case OP_BR:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb, MONO_R_ARM64_B);
+			arm_b (code, code);
+			break;
+		case OP_BR_REG:
+			arm_brx (code, sreg1);
+			break;
+		case OP_IBEQ:
+		case OP_IBGE:
+		case OP_IBGT:
+		case OP_IBLE:
+		case OP_IBLT:
+		case OP_IBNE_UN:
+		case OP_IBGE_UN:
+		case OP_IBGT_UN:
+		case OP_IBLE_UN:
+		case OP_IBLT_UN:
+		case OP_LBEQ:
+		case OP_LBGE:
+		case OP_LBGT:
+		case OP_LBLE:
+		case OP_LBLT:
+		case OP_LBNE_UN:
+		case OP_LBGE_UN:
+		case OP_LBGT_UN:
+		case OP_LBLE_UN:
+		case OP_LBLT_UN:
+		case OP_FBEQ:
+		case OP_FBNE_UN:
+		case OP_FBLT:
+		case OP_FBGT:
+		case OP_FBGT_UN:
+		case OP_FBLE:
+		case OP_FBGE:
+		case OP_FBGE_UN: {
+			int cond;
+
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_BCC);
+			cond = opcode_to_armcond (ins->opcode);
+			arm_bcc (code, cond, 0);
+			break;
+		}
+		case OP_FBLT_UN:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_BCC);
+			/* For fp compares, ARMCOND_LT is lt or unordered */
+			arm_bcc (code, ARMCOND_LT, 0);
+			break;
+		case OP_FBLE_UN:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_BCC);
+			arm_bcc (code, ARMCOND_EQ, 0);
+			offset = code - cfg->native_code;
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_BCC);
+			/* For fp compares, ARMCOND_LT is lt or unordered */
+			arm_bcc (code, ARMCOND_LT, 0);
+			break;
+		case OP_ARM64_CBZW:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_CBZ);
+			arm_cbzw (code, sreg1, 0);
+			break;
+		case OP_ARM64_CBZX:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_CBZ);
+			arm_cbzx (code, sreg1, 0);
+			break;
+		case OP_ARM64_CBNZW:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_CBZ);
+			arm_cbnzw (code, sreg1, 0);
+			break;
+		case OP_ARM64_CBNZX:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_true_bb, MONO_R_ARM64_CBZ);
+			arm_cbnzx (code, sreg1, 0);
+			break;
+			/* ALU */
+		case OP_IADD:
+			arm_addw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LADD:
+			arm_addx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_ISUB:
+			arm_subw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LSUB:
+			arm_subx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_IAND:
+			arm_andw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LAND:
+			arm_andx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_IOR:
+			arm_orrw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LOR:
+			arm_orrx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_IXOR:
+			arm_eorw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LXOR:
+			arm_eorx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_INEG:
+			arm_negw (code, dreg, sreg1);
+			break;
+		case OP_LNEG:
+			arm_negx (code, dreg, sreg1);
+			break;
+		case OP_INOT:
+			arm_mvnw (code, dreg, sreg1);
+			break;
+		case OP_LNOT:
+			arm_mvnx (code, dreg, sreg1);
+			break;
+		case OP_IADDCC:
+			arm_addsw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_ADDCC:
+		case OP_LADDCC:
+			arm_addsx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_ISUBCC:
+			arm_subsw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LSUBCC:
+		case OP_SUBCC:
+			arm_subsx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_ICOMPARE:
+			arm_cmpw (code, sreg1, sreg2);
+			break;
+		case OP_COMPARE:
+		case OP_LCOMPARE:
+			arm_cmpx (code, sreg1, sreg2);
+			break;
+		case OP_IADD_IMM:
+			code = emit_addw_imm (code, dreg, sreg1, imm);
+			break;
+		case OP_LADD_IMM:
+		case OP_ADD_IMM:
+			code = emit_addx_imm (code, dreg, sreg1, imm);
+			break;
+		case OP_ISUB_IMM:
+			code = emit_subw_imm (code, dreg, sreg1, imm);
+			break;
+		case OP_LSUB_IMM:
+			code = emit_subx_imm (code, dreg, sreg1, imm);
+			break;
+		case OP_IAND_IMM:
+			code = emit_andw_imm (code, dreg, sreg1, imm);
+			break;
+		case OP_LAND_IMM:
+		case OP_AND_IMM:
+			code = emit_andx_imm (code, dreg, sreg1, imm);
+			break;
+		case OP_IOR_IMM:
+			code = emit_orrw_imm (code, dreg, sreg1, imm);
+			break;
+		case OP_LOR_IMM:
+			code = emit_orrx_imm (code, dreg, sreg1, imm);
+			break;
+		case OP_IXOR_IMM:
+			code = emit_eorw_imm (code, dreg, sreg1, imm);
+			break;
+		case OP_LXOR_IMM:
+			code = emit_eorx_imm (code, dreg, sreg1, imm);
+			break;
+		case OP_ICOMPARE_IMM:
+			code = emit_cmpw_imm (code, sreg1, imm);
+			break;
+		case OP_LCOMPARE_IMM:
+		case OP_COMPARE_IMM:
+			if (imm == 0) {
+				arm_cmpx (code, sreg1, ARMREG_RZR);
+			} else {
+				// FIXME: 32 vs 64 bit issues for 0xffffffff
+				code = emit_imm64 (code, ARMREG_LR, imm);
+				arm_cmpx (code, sreg1, ARMREG_LR);
+			}
+			break;
+		case OP_ISHL:
+			arm_lslvw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LSHL:
+			arm_lslvx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_ISHR:
+			arm_asrvw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LSHR:
+			arm_asrvx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_ISHR_UN:
+			arm_lsrvw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LSHR_UN:
+			arm_lsrvx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_ISHL_IMM:
+			if (imm == 0)
+				arm_movx (code, dreg, sreg1);
+			else
+				arm_lslw (code, dreg, sreg1, imm);
+			break;
+		case OP_LSHL_IMM:
+			if (imm == 0)
+				arm_movx (code, dreg, sreg1);
+			else
+				arm_lslx (code, dreg, sreg1, imm);
+			break;
+		case OP_ISHR_IMM:
+			if (imm == 0)
+				arm_movx (code, dreg, sreg1);
+			else
+				arm_asrw (code, dreg, sreg1, imm);
+			break;
+		case OP_LSHR_IMM:
+		case OP_SHR_IMM:
+			if (imm == 0)
+				arm_movx (code, dreg, sreg1);
+			else
+				arm_asrx (code, dreg, sreg1, imm);
+			break;
+		case OP_ISHR_UN_IMM:
+			if (imm == 0)
+				arm_movx (code, dreg, sreg1);
+			else
+				arm_lsrw (code, dreg, sreg1, imm);
+			break;
+		case OP_SHR_UN_IMM:
+		case OP_LSHR_UN_IMM:
+			if (imm == 0)
+				arm_movx (code, dreg, sreg1);
+			else
+				arm_lsrx (code, dreg, sreg1, imm);
+			break;
+
+			/* 64BIT ALU */
+		case OP_SEXT_I4:
+			arm_sxtwx (code, dreg, sreg1);
+			break;
+		case OP_ZEXT_I4:
+			/* Clean out the upper word */
+			arm_movw (code, dreg, sreg1);
+			break;
+		case OP_SHL_IMM:
+			arm_lslx (code, dreg, sreg1, imm);
+			break;
+
+			/* MULTIPLY/DIVISION */
+		case OP_IDIV:
+		case OP_IREM:
+			// FIXME: Optimize this
+			/* Check for zero */
+			arm_cmpx_imm (code, sreg2, 0);
+			code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
+			/* Check for INT_MIN/-1 */
+			code = emit_imm (code, ARMREG_IP0, 0x80000000);
+			arm_cmpx (code, sreg1, ARMREG_IP0);
+			arm_cset (code, ARMCOND_EQ, ARMREG_IP1);
+			code = emit_imm (code, ARMREG_IP0, 0xffffffff);
+			arm_cmpx (code, sreg2, ARMREG_IP0);
+			arm_cset (code, ARMCOND_EQ, ARMREG_IP0);
+			arm_andx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
+			arm_cmpx_imm (code, ARMREG_IP0, 1);
+			code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "OverflowException");
+			if (ins->opcode == OP_IREM) {
+				arm_sdivw (code, ARMREG_LR, sreg1, sreg2);
+				arm_msubw (code, dreg, ARMREG_LR, sreg2, sreg1);
+			} else {
+				arm_sdivw (code, dreg, sreg1, sreg2);
+			}
+			break;
+		case OP_IDIV_UN:
+			arm_cmpx_imm (code, sreg2, 0);
+			code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
+			arm_udivw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_IREM_UN:
+			arm_cmpx_imm (code, sreg2, 0);
+			code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
+			arm_udivw (code, ARMREG_LR, sreg1, sreg2);
+			arm_msubw (code, dreg, ARMREG_LR, sreg2, sreg1);
+			break;
+		case OP_LDIV:
+		case OP_LREM:
+			// FIXME: Optimize this
+			/* Check for zero */
+			arm_cmpx_imm (code, sreg2, 0);
+			code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
+			/* Check for INT64_MIN/-1 */
+			code = emit_imm64 (code, ARMREG_IP0, 0x8000000000000000);
+			arm_cmpx (code, sreg1, ARMREG_IP0);
+			arm_cset (code, ARMCOND_EQ, ARMREG_IP1);
+			code = emit_imm64 (code, ARMREG_IP0, 0xffffffffffffffff);
+			arm_cmpx (code, sreg2, ARMREG_IP0);
+			arm_cset (code, ARMCOND_EQ, ARMREG_IP0);
+			arm_andx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
+			arm_cmpx_imm (code, ARMREG_IP0, 1);
+			/* 64 bit uses ArithmeticException */
+			code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "ArithmeticException");
+			if (ins->opcode == OP_LREM) {
+				arm_sdivx (code, ARMREG_LR, sreg1, sreg2);
+				arm_msubx (code, dreg, ARMREG_LR, sreg2, sreg1);
+			} else {
+				arm_sdivx (code, dreg, sreg1, sreg2);
+			}
+			break;
+		case OP_LDIV_UN:
+			arm_cmpx_imm (code, sreg2, 0);
+			code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
+			arm_udivx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LREM_UN:
+			arm_cmpx_imm (code, sreg2, 0);
+			code = emit_cond_exc (cfg, code, OP_COND_EXC_IEQ, "DivideByZeroException");
+			arm_udivx (code, ARMREG_LR, sreg1, sreg2);
+			arm_msubx (code, dreg, ARMREG_LR, sreg2, sreg1);
+			break;
+		case OP_IMUL:
+			arm_mulw (code, dreg, sreg1, sreg2);
+			break;
+		case OP_LMUL:
+			arm_mulx (code, dreg, sreg1, sreg2);
+			break;
+		case OP_IMUL_IMM:
+			code = emit_imm (code, ARMREG_LR, imm);
+			arm_mulw (code, dreg, sreg1, ARMREG_LR);
+			break;
+		case OP_MUL_IMM:
+		case OP_LMUL_IMM:
+			code = emit_imm (code, ARMREG_LR, imm);
+			arm_mulx (code, dreg, sreg1, ARMREG_LR);
+			break;
+
+			/* CONVERSIONS */
+		case OP_ICONV_TO_I1:
+		case OP_LCONV_TO_I1:
+			arm_sxtbx (code, dreg, sreg1);
+			break;
+		case OP_ICONV_TO_I2:
+		case OP_LCONV_TO_I2:
+			arm_sxthx (code, dreg, sreg1);
+			break;
+		case OP_ICONV_TO_U1:
+		case OP_LCONV_TO_U1:
+			arm_uxtbw (code, dreg, sreg1);
+			break;
+		case OP_ICONV_TO_U2:
+		case OP_LCONV_TO_U2:
+			arm_uxthw (code, dreg, sreg1);
+			break;
+
+			/* CSET */
+		case OP_CEQ:
+		case OP_ICEQ:
+		case OP_LCEQ:
+		case OP_CLT:
+		case OP_ICLT:
+		case OP_LCLT:
+		case OP_CGT:
+		case OP_ICGT:
+		case OP_LCGT:
+		case OP_CLT_UN:
+		case OP_ICLT_UN:
+		case OP_LCLT_UN:
+		case OP_CGT_UN:
+		case OP_ICGT_UN:
+		case OP_LCGT_UN:
+		case OP_ICNEQ:
+		case OP_ICGE:
+		case OP_ICLE:
+		case OP_ICGE_UN:
+		case OP_ICLE_UN: {
+			int cond;
+
+			cond = opcode_to_armcond (ins->opcode);
+			arm_cset (code, cond, dreg);
+			break;
+		}
+		case OP_FCEQ:
+		case OP_FCLT:
+		case OP_FCLT_UN:
+		case OP_FCGT:
+		case OP_FCGT_UN:
+		case OP_FCNEQ:
+		case OP_FCLE:
+		case OP_FCGE: {
+			int cond;
+
+			cond = opcode_to_armcond (ins->opcode);
+			arm_fcmpd (code, sreg1, sreg2);
+			arm_cset (code, cond, dreg);
+			break;
+		}
+
+			/* MEMORY */
+		case OP_LOADI1_MEMBASE:
+			code = emit_ldrsbx (code, dreg, ins->inst_basereg, ins->inst_offset);
+			break;
+		case OP_LOADU1_MEMBASE:
+			code = emit_ldrb (code, dreg, ins->inst_basereg, ins->inst_offset);
+			break;
+		case OP_LOADI2_MEMBASE:
+			code = emit_ldrshx (code, dreg, ins->inst_basereg, ins->inst_offset);
+			break;
+		case OP_LOADU2_MEMBASE:
+			code = emit_ldrh (code, dreg, ins->inst_basereg, ins->inst_offset);
+			break;
+		case OP_LOADI4_MEMBASE:
+			code = emit_ldrswx (code, dreg, ins->inst_basereg, ins->inst_offset);
+			break;
+		case OP_LOADU4_MEMBASE:
+			code = emit_ldrw (code, dreg, ins->inst_basereg, ins->inst_offset);
+			break;
+		case OP_LOAD_MEMBASE:
+		case OP_LOADI8_MEMBASE:
+			code = emit_ldrx (code, dreg, ins->inst_basereg, ins->inst_offset);
+			break;
+		case OP_STOREI1_MEMBASE_IMM:
+		case OP_STOREI2_MEMBASE_IMM:
+		case OP_STOREI4_MEMBASE_IMM:
+		case OP_STORE_MEMBASE_IMM:
+		case OP_STOREI8_MEMBASE_IMM: {
+			int immreg;
+
+			if (imm != 0) {
+				code = emit_imm (code, ARMREG_LR, imm);
+				immreg = ARMREG_LR;
+			} else {
+				immreg = ARMREG_RZR;
+			}
+
+			switch (ins->opcode) {
+			case OP_STOREI1_MEMBASE_IMM:
+				code = emit_strb (code, immreg, ins->inst_destbasereg, ins->inst_offset);
+				break;
+			case OP_STOREI2_MEMBASE_IMM:
+				code = emit_strh (code, immreg, ins->inst_destbasereg, ins->inst_offset);
+				break;
+			case OP_STOREI4_MEMBASE_IMM:
+				code = emit_strw (code, immreg, ins->inst_destbasereg, ins->inst_offset);
+				break;
+			case OP_STORE_MEMBASE_IMM:
+			case OP_STOREI8_MEMBASE_IMM:
+				code = emit_strx (code, immreg, ins->inst_destbasereg, ins->inst_offset);
+				break;
+			default:
+				g_assert_not_reached ();
+				break;
+			}
+			break;
+		}
+		case OP_STOREI1_MEMBASE_REG:
+			code = emit_strb (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
+			break;
+		case OP_STOREI2_MEMBASE_REG:
+			code = emit_strh (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
+			break;
+		case OP_STOREI4_MEMBASE_REG:
+			code = emit_strw (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
+			break;
+		case OP_STORE_MEMBASE_REG:
+		case OP_STOREI8_MEMBASE_REG:
+			code = emit_strx (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
+			break;
+
+		case OP_TLS_GET:
+			code = emit_tls_get (code, dreg, ins->inst_offset);
+			break;
+		case OP_TLS_GET_REG:
+			code = emit_tls_get_reg (code, dreg, sreg1);
+			break;
+		case OP_TLS_SET:
+			code = emit_tls_set (code, sreg1, ins->inst_offset);
+			break;
+		case OP_TLS_SET_REG:
+			code = emit_tls_set_reg (code, sreg1, sreg2);
+			break;
+
+			/* Atomic */
+		case OP_MEMORY_BARRIER:
+			arm_dmb (code, 0);
+			break;
+		case OP_ATOMIC_ADD_I4: {
+			guint8 *buf [16];
+
+			buf [0] = code;
+			arm_ldaxrw (code, ARMREG_IP0, sreg1);
+			arm_addx (code, ARMREG_IP0, ARMREG_IP0, sreg2);
+			arm_stlxrw (code, ARMREG_IP1, ARMREG_IP0, sreg1);
+			arm_cbnzw (code, ARMREG_IP1, buf [0]);
+
+			arm_movx (code, dreg, ARMREG_IP0);
+			break;
+		}
+		case OP_ATOMIC_ADD_I8: {
+			guint8 *buf [16];
+
+			buf [0] = code;
+			arm_ldaxrx (code, ARMREG_IP0, sreg1);
+			arm_addx (code, ARMREG_IP0, ARMREG_IP0, sreg2);
+			arm_stlxrx (code, ARMREG_IP1, ARMREG_IP0, sreg1);
+			arm_cbnzx (code, ARMREG_IP1, buf [0]);
+
+			arm_movx (code, dreg, ARMREG_IP0);
+			break;
+		}
+		case OP_ATOMIC_EXCHANGE_I4: {
+			guint8 *buf [16];
+
+			buf [0] = code;
+			arm_ldaxrw (code, ARMREG_IP0, sreg1);
+			arm_stlxrw (code, ARMREG_IP1, sreg2, sreg1);
+			arm_cbnzw (code, ARMREG_IP1, buf [0]);
+
+			arm_movx (code, dreg, ARMREG_IP0);
+			break;
+		}
+		case OP_ATOMIC_EXCHANGE_I8: {
+			guint8 *buf [16];
+
+			buf [0] = code;
+			arm_ldaxrx (code, ARMREG_IP0, sreg1);
+			arm_stlxrx (code, ARMREG_IP1, sreg2, sreg1);
+			arm_cbnzw (code, ARMREG_IP1, buf [0]);
+
+			arm_movx (code, dreg, ARMREG_IP0);
+			break;
+		}
+		case OP_ATOMIC_CAS_I4: {
+			guint8 *buf [16];
+
+			/* sreg2 is the value, sreg3 is the comparand */
+			buf [0] = code;
+			arm_ldaxrw (code, ARMREG_IP0, sreg1);
+			arm_cmpw (code, ARMREG_IP0, ins->sreg3);
+			buf [1] = code;
+			arm_bcc (code, ARMCOND_NE, 0);
+			arm_stlxrw (code, ARMREG_IP1, sreg2, sreg1);
+			arm_cbnzw (code, ARMREG_IP1, buf [0]);
+			arm_patch_rel (buf [1], code, MONO_R_ARM64_BCC);
+
+			arm_movx (code, dreg, ARMREG_IP0);
+			break;
+		}
+		case OP_ATOMIC_CAS_I8: {
+			guint8 *buf [16];
+
+			buf [0] = code;
+			arm_ldaxrx (code, ARMREG_IP0, sreg1);
+			arm_cmpx (code, ARMREG_IP0, ins->sreg3);
+			buf [1] = code;
+			arm_bcc (code, ARMCOND_NE, 0);
+			arm_stlxrx (code, ARMREG_IP1, sreg2, sreg1);
+			arm_cbnzw (code, ARMREG_IP1, buf [0]);
+			arm_patch_rel (buf [1], code, MONO_R_ARM64_BCC);
+
+			arm_movx (code, dreg, ARMREG_IP0);
+			break;
+		}
+		case OP_ATOMIC_LOAD_I1: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
+			arm_ldarb (code, ins->dreg, ARMREG_LR);
+			arm_sxtbx (code, ins->dreg, ins->dreg);
+			break;
+		}
+		case OP_ATOMIC_LOAD_U1: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
+			arm_ldarb (code, ins->dreg, ARMREG_LR);
+			arm_uxtbx (code, ins->dreg, ins->dreg);
+			break;
+		}
+		case OP_ATOMIC_LOAD_I2: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
+			arm_ldarh (code, ins->dreg, ARMREG_LR);
+			arm_sxthx (code, ins->dreg, ins->dreg);
+			break;
+		}
+		case OP_ATOMIC_LOAD_U2: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
+			arm_ldarh (code, ins->dreg, ARMREG_LR);
+			arm_uxthx (code, ins->dreg, ins->dreg);
+			break;
+		}
+		case OP_ATOMIC_LOAD_I4: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
+			arm_ldarw (code, ins->dreg, ARMREG_LR);
+			arm_sxtwx (code, ins->dreg, ins->dreg);
+			break;
+		}
+		case OP_ATOMIC_LOAD_U4: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
+			arm_ldarw (code, ins->dreg, ARMREG_LR);
+			arm_movw (code, ins->dreg, ins->dreg); /* Clear upper half of the register. */
+			break;
+		}
+		case OP_ATOMIC_LOAD_I8:
+		case OP_ATOMIC_LOAD_U8: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
+			arm_ldarx (code, ins->dreg, ARMREG_LR);
+			break;
+		}
+		case OP_ATOMIC_LOAD_R4: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
+			if (cfg->r4fp) {
+				arm_ldarw (code, ARMREG_LR, ARMREG_LR);
+				arm_fmov_rx_to_double (code, ins->dreg, ARMREG_LR);
+			} else {
+				arm_ldarw (code, ARMREG_LR, ARMREG_LR);
+				arm_fmov_rx_to_double (code, FP_TEMP_REG, ARMREG_LR);
+				arm_fcvt_sd (code, ins->dreg, FP_TEMP_REG);
+			}
+			break;
+		}
+		case OP_ATOMIC_LOAD_R8: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_basereg, ins->inst_offset);
+			arm_ldarx (code, ARMREG_LR, ARMREG_LR);
+			arm_fmov_rx_to_double (code, ins->dreg, ARMREG_LR);
+			break;
+		}
+		case OP_ATOMIC_STORE_I1:
+		case OP_ATOMIC_STORE_U1: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
+			arm_stlrb (code, ARMREG_LR, ins->sreg1);
+			break;
+		}
+		case OP_ATOMIC_STORE_I2:
+		case OP_ATOMIC_STORE_U2: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
+			arm_stlrh (code, ARMREG_LR, ins->sreg1);
+			break;
+		}
+		case OP_ATOMIC_STORE_I4:
+		case OP_ATOMIC_STORE_U4: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
+			arm_stlrw (code, ARMREG_LR, ins->sreg1);
+			break;
+		}
+		case OP_ATOMIC_STORE_I8:
+		case OP_ATOMIC_STORE_U8: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
+			arm_stlrx (code, ARMREG_LR, ins->sreg1);
+			break;
+		}
+		case OP_ATOMIC_STORE_R4: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
+			if (cfg->r4fp) {
+				arm_fmov_double_to_rx (code, ARMREG_IP0, ins->sreg1);
+				arm_stlrw (code, ARMREG_LR, ARMREG_IP0);
+			} else {
+				arm_fcvt_ds (code, FP_TEMP_REG, ins->sreg1);
+				arm_fmov_double_to_rx (code, ARMREG_IP0, FP_TEMP_REG);
+				arm_stlrw (code, ARMREG_LR, ARMREG_IP0);
+			}
+			break;
+		}
+		case OP_ATOMIC_STORE_R8: {
+			code = emit_addx_imm (code, ARMREG_LR, ins->inst_destbasereg, ins->inst_offset);
+			arm_fmov_double_to_rx (code, ARMREG_IP0, ins->sreg1);
+			arm_stlrx (code, ARMREG_LR, ARMREG_IP0);
+			break;
+		}
+
+			/* FP */
+		case OP_R8CONST: {
+			guint64 imm = *(guint64*)ins->inst_p0;
+
+			if (imm == 0) {
+				arm_fmov_rx_to_double (code, dreg, ARMREG_RZR);
+			} else {
+				code = emit_imm64 (code, ARMREG_LR, imm);
+				arm_fmov_rx_to_double (code, ins->dreg, ARMREG_LR);
+			}
+			break;
+		}
+		case OP_R4CONST: {
+			guint64 imm = *(guint32*)ins->inst_p0;
+
+			code = emit_imm64 (code, ARMREG_LR, imm);
+			if (cfg->r4fp) {
+				arm_fmov_rx_to_double (code, dreg, ARMREG_LR);
+			} else {
+				arm_fmov_rx_to_double (code, FP_TEMP_REG, ARMREG_LR);
+				arm_fcvt_sd (code, dreg, FP_TEMP_REG);
+			}
+			break;
+		}
+		case OP_LOADR8_MEMBASE:
+			code = emit_ldrfpx (code, dreg, ins->inst_basereg, ins->inst_offset);
+			break;
+		case OP_LOADR4_MEMBASE:
+			if (cfg->r4fp) {
+				code = emit_ldrfpw (code, dreg, ins->inst_basereg, ins->inst_offset);
+			} else {
+				code = emit_ldrfpw (code, FP_TEMP_REG, ins->inst_basereg, ins->inst_offset);
+				arm_fcvt_sd (code, dreg, FP_TEMP_REG);
+			}
+			break;
+		case OP_STORER8_MEMBASE_REG:
+			code = emit_strfpx (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
+			break;
+		case OP_STORER4_MEMBASE_REG:
+			if (cfg->r4fp) {
+				code = emit_strfpw (code, sreg1, ins->inst_destbasereg, ins->inst_offset);
+			} else {
+				arm_fcvt_ds (code, FP_TEMP_REG, sreg1);
+				code = emit_strfpw (code, FP_TEMP_REG, ins->inst_destbasereg, ins->inst_offset);
+			}
+			break;
+		case OP_FMOVE:
+			if (dreg != sreg1)
+				arm_fmovd (code, dreg, sreg1);
+			break;
+		case OP_RMOVE:
+			if (dreg != sreg1)
+				arm_fmovs (code, dreg, sreg1);
+			break;
+		case OP_MOVE_F_TO_I4:
+			if (cfg->r4fp) {
+				arm_fmov_double_to_rx (code, ins->dreg, ins->sreg1);
+			} else {
+				arm_fcvt_ds (code, ins->dreg, ins->sreg1);
+				arm_fmov_double_to_rx (code, ins->dreg, ins->dreg);
+			}
+			break;
+		case OP_MOVE_I4_TO_F:
+			if (cfg->r4fp) {
+				arm_fmov_rx_to_double (code, ins->dreg, ins->sreg1);
+			} else {
+				arm_fmov_rx_to_double (code, ins->dreg, ins->sreg1);
+				arm_fcvt_sd (code, ins->dreg, ins->dreg);
+			}
+			break;
+		case OP_MOVE_F_TO_I8:
+			arm_fmov_double_to_rx (code, ins->dreg, ins->sreg1);
+			break;
+		case OP_MOVE_I8_TO_F:
+			arm_fmov_rx_to_double (code, ins->dreg, ins->sreg1);
+			break;
+		case OP_FCOMPARE:
+			arm_fcmpd (code, sreg1, sreg2);
+			break;
+		case OP_RCOMPARE:
+			arm_fcmps (code, sreg1, sreg2);
+			break;
+		case OP_FCONV_TO_I1:
+			arm_fcvtzs_dx (code, dreg, sreg1);
+			arm_sxtbx (code, dreg, dreg);
+			break;
+		case OP_FCONV_TO_U1:
+			arm_fcvtzu_dx (code, dreg, sreg1);
+			arm_uxtbw (code, dreg, dreg);
+			break;
+		case OP_FCONV_TO_I2:
+			arm_fcvtzs_dx (code, dreg, sreg1);
+			arm_sxthx (code, dreg, dreg);
+			break;
+		case OP_FCONV_TO_U2:
+			arm_fcvtzu_dx (code, dreg, sreg1);
+			arm_uxthw (code, dreg, dreg);
+			break;
+		case OP_FCONV_TO_I4:
+			arm_fcvtzs_dx (code, dreg, sreg1);
+			arm_sxtwx (code, dreg, dreg);
+			break;
+		case OP_FCONV_TO_U4:
+			arm_fcvtzu_dx (code, dreg, sreg1);
+			break;
+		case OP_FCONV_TO_I8:
+			arm_fcvtzs_dx (code, dreg, sreg1);
+			break;
+		case OP_FCONV_TO_U8:
+			arm_fcvtzu_dx (code, dreg, sreg1);
+			break;
+		case OP_FCONV_TO_R4:
+			if (cfg->r4fp) {
+				arm_fcvt_ds (code, dreg, sreg1);
+			} else {
+				arm_fcvt_ds (code, FP_TEMP_REG, sreg1);
+				arm_fcvt_sd (code, dreg, FP_TEMP_REG);
+			}
+			break;
+		case OP_ICONV_TO_R4:
+			if (cfg->r4fp) {
+				arm_scvtf_rw_to_s (code, dreg, sreg1);
+			} else {
+				arm_scvtf_rw_to_s (code, FP_TEMP_REG, sreg1);
+				arm_fcvt_sd (code, dreg, FP_TEMP_REG);
+			}
+			break;
+		case OP_LCONV_TO_R4:
+			if (cfg->r4fp) {
+				arm_scvtf_rx_to_s (code, dreg, sreg1);
+			} else {
+				arm_scvtf_rx_to_s (code, FP_TEMP_REG, sreg1);
+				arm_fcvt_sd (code, dreg, FP_TEMP_REG);
+			}
+			break;
+		case OP_ICONV_TO_R8:
+			arm_scvtf_rw_to_d (code, dreg, sreg1);
+			break;
+		case OP_LCONV_TO_R8:
+			arm_scvtf_rx_to_d (code, dreg, sreg1);
+			break;
+		case OP_ICONV_TO_R_UN:
+			arm_ucvtf_rw_to_d (code, dreg, sreg1);
+			break;
+		case OP_LCONV_TO_R_UN:
+			arm_ucvtf_rx_to_d (code, dreg, sreg1);
+			break;
+		case OP_FADD:
+			arm_fadd_d (code, dreg, sreg1, sreg2);
+			break;
+		case OP_FSUB:
+			arm_fsub_d (code, dreg, sreg1, sreg2);
+			break;
+		case OP_FMUL:
+			arm_fmul_d (code, dreg, sreg1, sreg2);
+			break;
+		case OP_FDIV:
+			arm_fdiv_d (code, dreg, sreg1, sreg2);
+			break;
+		case OP_FREM:
+			/* Emulated */
+			g_assert_not_reached ();
+			break;
+		case OP_FNEG:
+			arm_fneg_d (code, dreg, sreg1);
+			break;
+		case OP_ARM_SETFREG_R4:
+			arm_fcvt_ds (code, dreg, sreg1);
+			break;
+		case OP_CKFINITE:
+			/* Check for infinity */
+			code = emit_imm64 (code, ARMREG_LR, 0x7fefffffffffffffLL);
+			arm_fmov_rx_to_double (code, FP_TEMP_REG, ARMREG_LR);
+			arm_fabs_d (code, FP_TEMP_REG2, sreg1);
+			arm_fcmpd (code, FP_TEMP_REG2, FP_TEMP_REG);
+			code = emit_cond_exc (cfg, code, OP_COND_EXC_GT, "ArithmeticException");
+			/* Check for nans */
+			arm_fcmpd (code, FP_TEMP_REG2, FP_TEMP_REG2);
+			code = emit_cond_exc (cfg, code, OP_COND_EXC_OV, "ArithmeticException");
+			arm_fmovd (code, dreg, sreg1);
+			break;
+
+			/* R4 */
+		case OP_RADD:
+			arm_fadd_s (code, dreg, sreg1, sreg2);
+			break;
+		case OP_RSUB:
+			arm_fsub_s (code, dreg, sreg1, sreg2);
+			break;
+		case OP_RMUL:
+			arm_fmul_s (code, dreg, sreg1, sreg2);
+			break;
+		case OP_RDIV:
+			arm_fdiv_s (code, dreg, sreg1, sreg2);
+			break;
+		case OP_RNEG:
+			arm_fneg_s (code, dreg, sreg1);
+			break;
+		case OP_RCONV_TO_I1:
+			arm_fcvtzs_sx (code, dreg, sreg1);
+			arm_sxtbx (code, dreg, dreg);
+			break;
+		case OP_RCONV_TO_U1:
+			arm_fcvtzu_sx (code, dreg, sreg1);
+			arm_uxtbw (code, dreg, dreg);
+			break;
+		case OP_RCONV_TO_I2:
+			arm_fcvtzs_sx (code, dreg, sreg1);
+			arm_sxthx (code, dreg, dreg);
+			break;
+		case OP_RCONV_TO_U2:
+			arm_fcvtzu_sx (code, dreg, sreg1);
+			arm_uxthw (code, dreg, dreg);
+			break;
+		case OP_RCONV_TO_I4:
+			arm_fcvtzs_sx (code, dreg, sreg1);
+			arm_sxtwx (code, dreg, dreg);
+			break;
+		case OP_RCONV_TO_U4:
+			arm_fcvtzu_sx (code, dreg, sreg1);
+			break;
+		case OP_RCONV_TO_I8:
+			arm_fcvtzs_sx (code, dreg, sreg1);
+			break;
+		case OP_RCONV_TO_U8:
+			arm_fcvtzu_sx (code, dreg, sreg1);
+			break;
+		case OP_RCONV_TO_R8:
+			arm_fcvt_sd (code, dreg, sreg1);
+			break;
+		case OP_RCONV_TO_R4:
+			if (dreg != sreg1)
+				arm_fmovs (code, dreg, sreg1);
+			break;
+		case OP_RCEQ:
+		case OP_RCLT:
+		case OP_RCLT_UN:
+		case OP_RCGT:
+		case OP_RCGT_UN:
+		case OP_RCNEQ:
+		case OP_RCLE:
+		case OP_RCGE: {
+			int cond;
+
+			cond = opcode_to_armcond (ins->opcode);
+			arm_fcmps (code, sreg1, sreg2);
+			arm_cset (code, cond, dreg);
+			break;
+		}
+
+			/* CALLS */
+		case OP_VOIDCALL:
+		case OP_CALL:
+		case OP_LCALL:
+		case OP_FCALL:
+		case OP_RCALL:
+		case OP_VCALL2:
+			call = (MonoCallInst*)ins;
+			if (ins->flags & MONO_INST_HAS_METHOD)
+				code = emit_call (cfg, code, MONO_PATCH_INFO_METHOD, call->method);
+			else
+				code = emit_call (cfg, code, MONO_PATCH_INFO_ABS, call->fptr);
+			code = emit_move_return_value (cfg, code, ins);
+			break;
+		case OP_VOIDCALL_REG:
+		case OP_CALL_REG:
+		case OP_LCALL_REG:
+		case OP_FCALL_REG:
+		case OP_RCALL_REG:
+		case OP_VCALL2_REG:
+			arm_blrx (code, sreg1);
+			code = emit_move_return_value (cfg, code, ins);
+			break;
+		case OP_VOIDCALL_MEMBASE:
+		case OP_CALL_MEMBASE:
+		case OP_LCALL_MEMBASE:
+		case OP_FCALL_MEMBASE:
+		case OP_RCALL_MEMBASE:
+		case OP_VCALL2_MEMBASE:
+			code = emit_ldrx (code, ARMREG_IP0, ins->inst_basereg, ins->inst_offset);
+			arm_blrx (code, ARMREG_IP0);
+			code = emit_move_return_value (cfg, code, ins);
+			break;
+		case OP_TAILCALL: {
+			MonoCallInst *call = (MonoCallInst*)ins;
+
+			g_assert (!cfg->method->save_lmf);
+
+			// FIXME: Copy stack arguments
+
+			/* Restore registers */
+			code = emit_load_regset (code, MONO_ARCH_CALLEE_SAVED_REGS & cfg->used_int_regs, ARMREG_FP, cfg->arch.saved_gregs_offset);
+
+			/* Destroy frame */
+			code = mono_arm_emit_destroy_frame (code, cfg->stack_offset, ((1 << ARMREG_IP0) | (1 << ARMREG_IP1)));
+
+			if (cfg->compile_aot) {
+				/* This is not a PLT patch */
+				code = emit_aotconst (cfg, code, ARMREG_IP0, MONO_PATCH_INFO_METHOD_JUMP, call->method);
+				arm_brx (code, ARMREG_IP0);
+			} else {
+				mono_add_patch_info_rel (cfg, code - cfg->native_code, MONO_PATCH_INFO_METHOD_JUMP, call->method, MONO_R_ARM64_B);
+				arm_b (code, code);
+			}
+			ins->flags |= MONO_INST_GC_CALLSITE;
+			ins->backend.pc_offset = code - cfg->native_code;
+			break;
+		}
+		case OP_ARGLIST:
+			g_assert (cfg->arch.cinfo);
+			code = emit_addx_imm (code, ARMREG_IP0, cfg->arch.args_reg, ((CallInfo*)cfg->arch.cinfo)->sig_cookie.offset);
+			arm_strx (code, ARMREG_IP0, sreg1, 0);
+			break;
+		case OP_DYN_CALL: {
+			MonoInst *var = cfg->dyn_call_var;
+			guint8 *labels [16];
+			int i;
+
+			/*
+			 * sreg1 points to a DynCallArgs structure initialized by mono_arch_start_dyn_call ().
+			 * sreg2 is the function to call.
+			 */
+
+			g_assert (var->opcode == OP_REGOFFSET);
+
+			arm_movx (code, ARMREG_LR, sreg1);
+			arm_movx (code, ARMREG_IP1, sreg2);
+
+			/* Save args buffer */
+			code = emit_strx (code, ARMREG_LR, var->inst_basereg, var->inst_offset);
+
+			/* Set fp argument regs */
+			code = emit_ldrw (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, n_fpargs));
+			arm_cmpw (code, ARMREG_R0, ARMREG_RZR);
+			labels [0] = code;
+			arm_bcc (code, ARMCOND_EQ, 0);
+			for (i = 0; i < 8; ++i)
+				code = emit_ldrfpx (code, ARMREG_D0 + i, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, fpregs) + (i * 8));
+			arm_patch_rel (labels [0], code, MONO_R_ARM64_BCC);
+
+			/* Set stack args */
+			for (i = 0; i < DYN_CALL_STACK_ARGS; ++i) {
+				code = emit_ldrx (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, regs) + ((PARAM_REGS + 1 + i) * sizeof (mgreg_t)));
+				code = emit_strx (code, ARMREG_R0, ARMREG_SP, i * sizeof (mgreg_t));
+			}
+
+			/* Set argument registers + r8 */
+			code = mono_arm_emit_load_regarray (code, 0x1ff, ARMREG_LR, 0);
+
+			/* Make the call */
+			arm_blrx (code, ARMREG_IP1);
+
+			/* Save result */
+			code = emit_ldrx (code, ARMREG_LR, var->inst_basereg, var->inst_offset);
+			arm_strx (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, res));
+			arm_strx (code, ARMREG_R1, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, res2));
+			/* Save fp result */
+			code = emit_ldrw (code, ARMREG_R0, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, n_fpret));
+			arm_cmpw (code, ARMREG_R0, ARMREG_RZR);
+			labels [1] = code;
+			arm_bcc (code, ARMCOND_EQ, 0);
+			for (i = 0; i < 8; ++i)
+				code = emit_strfpx (code, ARMREG_D0 + i, ARMREG_LR, MONO_STRUCT_OFFSET (DynCallArgs, fpregs) + (i * 8));
+			arm_patch_rel (labels [1], code, MONO_R_ARM64_BCC);
+			break;
+		}
+
+		case OP_GENERIC_CLASS_INIT: {
+			static int byte_offset = -1;
+			static guint8 bitmask;
+			guint8 *jump;
+
+			if (byte_offset < 0)
+				mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
+
+			/* Load vtable->initialized */
+			arm_ldrsbx (code, ARMREG_IP0, sreg1, byte_offset);
+			// FIXME: No andx_imm yet */
+			code = mono_arm_emit_imm64 (code, ARMREG_IP1, bitmask);
+			arm_andx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
+			jump = code;
+			arm_cbnzx (code, ARMREG_IP0, 0);
+
+			/* Slowpath */
+			g_assert (sreg1 == ARMREG_R0);
+
+			/* Call mono_generic_class_init_trampoline () */
+			/* The vtable is still in R0, the generic trampoline code will pass it as the argument to the C function */
+			code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD,
+							  (gpointer)"specific_trampoline_generic_class_init");
+
+			mono_arm_patch (jump, code, MONO_R_ARM64_CBZ);
+			break;
+		}
+
+		case OP_CHECK_THIS:
+			arm_ldrx (code, ARMREG_LR, sreg1, 0);
+			break;
+		case OP_NOT_NULL:
+		case OP_NOT_REACHED:
+		case OP_DUMMY_USE:
+			break;
+		case OP_IL_SEQ_POINT:
+			mono_add_seq_point (cfg, bb, ins, code - cfg->native_code);
+			break;
+
+			/* EH */
+		case OP_COND_EXC_C:
+		case OP_COND_EXC_IC:
+		case OP_COND_EXC_OV:
+		case OP_COND_EXC_IOV:
+		case OP_COND_EXC_NC:
+		case OP_COND_EXC_INC:
+		case OP_COND_EXC_NO:
+		case OP_COND_EXC_INO:
+		case OP_COND_EXC_EQ:
+		case OP_COND_EXC_IEQ:
+		case OP_COND_EXC_NE_UN:
+		case OP_COND_EXC_INE_UN:
+		case OP_COND_EXC_ILT:
+		case OP_COND_EXC_LT:
+		case OP_COND_EXC_ILT_UN:
+		case OP_COND_EXC_LT_UN:
+		case OP_COND_EXC_IGT:
+		case OP_COND_EXC_GT:
+		case OP_COND_EXC_IGT_UN:
+		case OP_COND_EXC_GT_UN:
+		case OP_COND_EXC_IGE:
+		case OP_COND_EXC_GE:
+		case OP_COND_EXC_IGE_UN:
+		case OP_COND_EXC_GE_UN:
+		case OP_COND_EXC_ILE:
+		case OP_COND_EXC_LE:
+		case OP_COND_EXC_ILE_UN:
+		case OP_COND_EXC_LE_UN:
+			code = emit_cond_exc (cfg, code, ins->opcode, ins->inst_p1);
+			break;
+		case OP_THROW:
+			if (sreg1 != ARMREG_R0)
+				arm_movx (code, ARMREG_R0, sreg1);
+			code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, 
+							  (gpointer)"mono_arch_throw_exception");
+			break;
+		case OP_RETHROW:
+			if (sreg1 != ARMREG_R0)
+				arm_movx (code, ARMREG_R0, sreg1);
+			code = emit_call (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, 
+							  (gpointer)"mono_arch_rethrow_exception");
+			break;
+		case OP_CALL_HANDLER:
+			mono_add_patch_info_rel (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb, MONO_R_ARM64_BL);
+			arm_bl (code, 0);
+			cfg->thunk_area += THUNK_SIZE;
+			break;
+		case OP_START_HANDLER: {
+			MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
+
+			/* Save caller address */
+			code = emit_strx (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
+
+			/*
+			 * Reserve a param area, see test_0_finally_param_area ().
+			 * This is needed because the param area is not set up when
+			 * we are called from EH code.
+			 */
+			if (cfg->param_area)
+				code = emit_subx_sp_imm (code, cfg->param_area);
+			break;
+		}
+		case OP_ENDFINALLY:
+		case OP_ENDFILTER: {
+			MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
+
+			if (cfg->param_area)
+				code = emit_addx_sp_imm (code, cfg->param_area);
+
+			if (ins->opcode == OP_ENDFILTER && sreg1 != ARMREG_R0)
+				arm_movx (code, ARMREG_R0, sreg1);
+
+			/* Return to either after the branch in OP_CALL_HANDLER, or to the EH code */
+			code = emit_ldrx (code, ARMREG_LR, spvar->inst_basereg, spvar->inst_offset);
+			arm_brx (code, ARMREG_LR);
+			break;
+		}
+		case OP_GET_EX_OBJ:
+			if (ins->dreg != ARMREG_R0)
+				arm_movx (code, ins->dreg, ARMREG_R0);
+			break;
+
+		default:
+			g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
+			g_assert_not_reached ();
+		}
+
+		if ((cfg->opt & MONO_OPT_BRANCH) && ((code - cfg->native_code - offset) > max_len)) {
+			g_warning ("wrong maximal instruction length of instruction %s (expected %d, got %d)",
+				   mono_inst_name (ins->opcode), max_len, code - cfg->native_code - offset);
+			g_assert_not_reached ();
+		}
+	}
+
+	/*
+	 * If the compiled code size is larger than the bcc displacement (19 bits signed),
+	 * insert branch islands between/inside basic blocks.
+	 */
+	if (cfg->arch.cond_branch_islands)
+		code = emit_branch_island (cfg, code, start_offset);
+
+	cfg->code_len = code - cfg->native_code;
+}
+
+static guint8*
+emit_move_args (MonoCompile *cfg, guint8 *code)
+{
+	MonoInst *ins;
+	CallInfo *cinfo;
+	ArgInfo *ainfo;
+	int i, part;
+
+	cinfo = cfg->arch.cinfo;
+	g_assert (cinfo);
+	for (i = 0; i < cinfo->nargs; ++i) {
+		ainfo = cinfo->args + i;
+		ins = cfg->args [i];
+
+		if (ins->opcode == OP_REGVAR) {
+			switch (ainfo->storage) {
+			case ArgInIReg:
+				arm_movx (code, ins->dreg, ainfo->reg);
+				break;
+			case ArgOnStack:
+				switch (ainfo->slot_size) {
+				case 1:
+					if (ainfo->sign)
+						code = emit_ldrsbx (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+					else
+						code = emit_ldrb (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+					break;
+				case 2:
+					if (ainfo->sign)
+						code = emit_ldrshx (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+					else
+						code = emit_ldrh (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+					break;
+				case 4:
+					if (ainfo->sign)
+						code = emit_ldrswx (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+					else
+						code = emit_ldrw (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+					break;
+				default:
+					code = emit_ldrx (code, ins->dreg, cfg->arch.args_reg, ainfo->offset);
+					break;
+				}
+				break;
+			default:
+				g_assert_not_reached ();
+				break;
+			}
+		} else {
+			if (ainfo->storage != ArgVtypeByRef && ainfo->storage != ArgVtypeByRefOnStack)
+				g_assert (ins->opcode == OP_REGOFFSET);
+
+			switch (ainfo->storage) {
+			case ArgInIReg:
+				/* Stack slots for arguments have size 8 */
+				code = emit_strx (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+				break;
+			case ArgInFReg:
+				code = emit_strfpx (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+				break;
+			case ArgInFRegR4:
+				code = emit_strfpw (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+				break;
+			case ArgOnStack:
+			case ArgOnStackR4:
+			case ArgOnStackR8:
+			case ArgVtypeByRefOnStack:
+			case ArgVtypeOnStack:
+				break;
+			case ArgVtypeByRef: {
+				MonoInst *addr_arg = ins->inst_left;
+
+				if (ainfo->gsharedvt) {
+					g_assert (ins->opcode == OP_GSHAREDVT_ARG_REGOFFSET);
+					arm_strx (code, ainfo->reg, ins->inst_basereg, ins->inst_offset);
+				} else {
+					g_assert (ins->opcode == OP_VTARG_ADDR);
+					g_assert (addr_arg->opcode == OP_REGOFFSET);
+					arm_strx (code, ainfo->reg, addr_arg->inst_basereg, addr_arg->inst_offset);
+				}
+				break;
+			}
+			case ArgVtypeInIRegs:
+				for (part = 0; part < ainfo->nregs; part ++) {
+					code = emit_strx (code, ainfo->reg + part, ins->inst_basereg, ins->inst_offset + (part * 8));
+				}
+				break;
+			case ArgHFA:
+				for (part = 0; part < ainfo->nregs; part ++) {
+					if (ainfo->esize == 4)
+						code = emit_strfpw (code, ainfo->reg + part, ins->inst_basereg, ins->inst_offset + ainfo->foffsets [part]);
+					else
+						code = emit_strfpx (code, ainfo->reg + part, ins->inst_basereg, ins->inst_offset + ainfo->foffsets [part]);
+				}
+				break;
+			default:
+				g_assert_not_reached ();
+				break;
+			}
+		}
+	}
+
+	return code;
+}
+
+/*
+ * emit_store_regarray:
+ *
+ *   Emit code to store the registers in REGS into the appropriate elements of
+ * the register array at BASEREG+OFFSET.
+ */
+static __attribute__((warn_unused_result)) guint8*
+emit_store_regarray (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	int i;
+
+	for (i = 0; i < 32; ++i) {
+		if (regs & (1 << i)) {
+			if (i + 1 < 32 && (regs & (1 << (i + 1))) && (i + 1 != ARMREG_SP)) {
+				arm_stpx (code, i, i + 1, basereg, offset + (i * 8));
+				i++;
+			} else if (i == ARMREG_SP) {
+				arm_movspx (code, ARMREG_IP1, ARMREG_SP);
+				arm_strx (code, ARMREG_IP1, basereg, offset + (i * 8));
+			} else {
+				arm_strx (code, i, basereg, offset + (i * 8));
+			}
+		}
+	}
+	return code;
+}
+
+/*
+ * emit_load_regarray:
+ *
+ *   Emit code to load the registers in REGS from the appropriate elements of
+ * the register array at BASEREG+OFFSET.
+ */
+static __attribute__((warn_unused_result)) guint8*
+emit_load_regarray (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	int i;
+
+	for (i = 0; i < 32; ++i) {
+		if (regs & (1 << i)) {
+			if ((regs & (1 << (i + 1))) && (i + 1 != ARMREG_SP)) {
+				if (offset + (i * 8) < 500)
+					arm_ldpx (code, i, i + 1, basereg, offset + (i * 8));
+				else {
+					code = emit_ldrx (code, i, basereg, offset + (i * 8));
+					code = emit_ldrx (code, i + 1, basereg, offset + ((i + 1) * 8));
+				}
+				i++;
+			} else if (i == ARMREG_SP) {
+				g_assert_not_reached ();
+			} else {
+				code = emit_ldrx (code, i, basereg, offset + (i * 8));
+			}
+		}
+	}
+	return code;
+}
+
+/*
+ * emit_store_regset:
+ *
+ *   Emit code to store the registers in REGS into consecutive memory locations starting
+ * at BASEREG+OFFSET.
+ */
+static __attribute__((warn_unused_result)) guint8*
+emit_store_regset (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	int i, pos;
+
+	pos = 0;
+	for (i = 0; i < 32; ++i) {
+		if (regs & (1 << i)) {
+			if ((regs & (1 << (i + 1))) && (i + 1 != ARMREG_SP)) {
+				arm_stpx (code, i, i + 1, basereg, offset + (pos * 8));
+				i++;
+				pos++;
+			} else if (i == ARMREG_SP) {
+				arm_movspx (code, ARMREG_IP1, ARMREG_SP);
+				arm_strx (code, ARMREG_IP1, basereg, offset + (pos * 8));
+			} else {
+				arm_strx (code, i, basereg, offset + (pos * 8));
+			}
+			pos++;
+		}
+	}
+	return code;
+}
+
+/*
+ * emit_load_regset:
+ *
+ *   Emit code to load the registers in REGS from consecutive memory locations starting
+ * at BASEREG+OFFSET.
+ */
+static __attribute__((warn_unused_result)) guint8*
+emit_load_regset (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	int i, pos;
+
+	pos = 0;
+	for (i = 0; i < 32; ++i) {
+		if (regs & (1 << i)) {
+			if ((regs & (1 << (i + 1))) && (i + 1 != ARMREG_SP)) {
+				arm_ldpx (code, i, i + 1, basereg, offset + (pos * 8));
+				i++;
+				pos++;
+			} else if (i == ARMREG_SP) {
+				g_assert_not_reached ();
+			} else {
+				arm_ldrx (code, i, basereg, offset + (pos * 8));
+			}
+			pos++;
+		}
+	}
+	return code;
+}
+
+__attribute__((warn_unused_result)) guint8*
+mono_arm_emit_load_regarray (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	return emit_load_regarray (code, regs, basereg, offset);
+}
+
+__attribute__((warn_unused_result)) guint8*
+mono_arm_emit_store_regarray (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	return emit_store_regarray (code, regs, basereg, offset);
+}
+
+__attribute__((warn_unused_result)) guint8*
+mono_arm_emit_store_regset (guint8 *code, guint64 regs, int basereg, int offset)
+{
+	return emit_store_regset (code, regs, basereg, offset);
+}
+
+/* Same as emit_store_regset, but emit unwind info too */
+/* CFA_OFFSET is the offset between the CFA and basereg */
+static __attribute__((warn_unused_result)) guint8*
+emit_store_regset_cfa (MonoCompile *cfg, guint8 *code, guint64 regs, int basereg, int offset, int cfa_offset, guint64 no_cfa_regset)
+{
+	int i, j, pos, nregs;
+	guint32 cfa_regset = regs & ~no_cfa_regset;
+
+	pos = 0;
+	for (i = 0; i < 32; ++i) {
+		nregs = 1;
+		if (regs & (1 << i)) {
+			if ((regs & (1 << (i + 1))) && (i + 1 != ARMREG_SP)) {
+				if (offset < 256) {
+					arm_stpx (code, i, i + 1, basereg, offset + (pos * 8));
+				} else {
+					code = emit_strx (code, i, basereg, offset + (pos * 8));
+					code = emit_strx (code, i + 1, basereg, offset + (pos * 8) + 8);
+				}
+				nregs = 2;
+			} else if (i == ARMREG_SP) {
+				arm_movspx (code, ARMREG_IP1, ARMREG_SP);
+				code = emit_strx (code, ARMREG_IP1, basereg, offset + (pos * 8));
+			} else {
+				code = emit_strx (code, i, basereg, offset + (pos * 8));
+			}
+
+			for (j = 0; j < nregs; ++j) {
+				if (cfa_regset & (1 << (i + j)))
+					mono_emit_unwind_op_offset (cfg, code, i + j, (- cfa_offset) + offset + ((pos + j) * 8));
+			}
+
+			i += nregs - 1;
+			pos += nregs;
+		}
+	}
+	return code;
+}
+
+/*
+ * emit_setup_lmf:
+ *
+ *   Emit code to initialize an LMF structure at LMF_OFFSET.
+ * Clobbers ip0/ip1.
+ */
+static guint8*
+emit_setup_lmf (MonoCompile *cfg, guint8 *code, gint32 lmf_offset, int cfa_offset)
+{
+	/*
+	 * The LMF should contain all the state required to be able to reconstruct the machine state
+	 * at the current point of execution. Since the LMF is only read during EH, only callee
+	 * saved etc. registers need to be saved.
+	 * FIXME: Save callee saved fp regs, JITted code doesn't use them, but native code does, and they
+	 * need to be restored during EH.
+	 */
+
+	/* pc */
+	arm_adrx (code, ARMREG_LR, code);
+	code = emit_strx (code, ARMREG_LR, ARMREG_FP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMF, pc));
+	/* gregs + fp + sp */
+	/* Don't emit unwind info for sp/fp, they are already handled in the prolog */
+	code = emit_store_regset_cfa (cfg, code, MONO_ARCH_LMF_REGS, ARMREG_FP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMF, gregs), cfa_offset, (1 << ARMREG_FP) | (1 << ARMREG_SP));
+
+	return code;
+}
+
+guint8 *
+mono_arch_emit_prolog (MonoCompile *cfg)
+{
+	MonoMethod *method = cfg->method;
+	MonoMethodSignature *sig;
+	MonoBasicBlock *bb;
+	guint8 *code;
+	int cfa_offset, max_offset;
+
+	sig = mono_method_signature (method);
+	cfg->code_size = 256 + sig->param_count * 64;
+	code = cfg->native_code = g_malloc (cfg->code_size);
+
+	/* This can be unaligned */
+	cfg->stack_offset = ALIGN_TO (cfg->stack_offset, MONO_ARCH_FRAME_ALIGNMENT);
+
+	/*
+	 * - Setup frame
+	 */
+	cfa_offset = 0;
+	mono_emit_unwind_op_def_cfa (cfg, code, ARMREG_SP, 0);
+
+	/* Setup frame */
+	if (arm_is_ldpx_imm (-cfg->stack_offset)) {
+		arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -cfg->stack_offset);
+	} else {
+		/* sp -= cfg->stack_offset */
+		/* This clobbers ip0/ip1 */
+		code = emit_subx_sp_imm (code, cfg->stack_offset);
+		arm_stpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0);
+	}
+	cfa_offset += cfg->stack_offset;
+	mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+	mono_emit_unwind_op_offset (cfg, code, ARMREG_FP, (- cfa_offset) + 0);
+	mono_emit_unwind_op_offset (cfg, code, ARMREG_LR, (- cfa_offset) + 8);
+	arm_movspx (code, ARMREG_FP, ARMREG_SP);
+	mono_emit_unwind_op_def_cfa_reg (cfg, code, ARMREG_FP);
+	if (cfg->param_area) {
+		/* The param area is below the frame pointer */
+		code = emit_subx_sp_imm (code, cfg->param_area);
+	}
+
+	if (cfg->method->save_lmf) {
+		code = emit_setup_lmf (cfg, code, cfg->lmf_var->inst_offset, cfa_offset);
+	} else {
+		/* Save gregs */
+		code = emit_store_regset_cfa (cfg, code, MONO_ARCH_CALLEE_SAVED_REGS & cfg->used_int_regs, ARMREG_FP, cfg->arch.saved_gregs_offset, cfa_offset, 0);
+	}
+
+	/* Setup args reg */
+	if (cfg->arch.args_reg) {
+		/* The register was already saved above */
+		code = emit_addx_imm (code, cfg->arch.args_reg, ARMREG_FP, cfg->stack_offset);
+	}
+
+	/* Save return area addr received in R8 */
+	if (cfg->vret_addr) {
+		MonoInst *ins = cfg->vret_addr;
+
+		g_assert (ins->opcode == OP_REGOFFSET);
+		code = emit_strx (code, ARMREG_R8, ins->inst_basereg, ins->inst_offset);
+	}
+
+	/* Save mrgctx received in MONO_ARCH_RGCTX_REG */
+	if (cfg->rgctx_var) {
+		MonoInst *ins = cfg->rgctx_var;
+
+		g_assert (ins->opcode == OP_REGOFFSET);
+
+		code = emit_strx (code, MONO_ARCH_RGCTX_REG, ins->inst_basereg, ins->inst_offset); 
+	}
+		
+	/*
+	 * Move arguments to their registers/stack locations.
+	 */
+	code = emit_move_args (cfg, code);
+
+	/* Initialize seq_point_info_var */
+	if (cfg->arch.seq_point_info_var) {
+		MonoInst *ins = cfg->arch.seq_point_info_var;
+
+		/* Initialize the variable from a GOT slot */
+		code = emit_aotconst (cfg, code, ARMREG_IP0, MONO_PATCH_INFO_SEQ_POINT_INFO, cfg->method);
+		g_assert (ins->opcode == OP_REGOFFSET);
+		code = emit_strx (code, ARMREG_IP0, ins->inst_basereg, ins->inst_offset);
+
+		/* Initialize ss_tramp_var */
+		ins = cfg->arch.ss_tramp_var;
+		g_assert (ins->opcode == OP_REGOFFSET);
+
+		code = emit_ldrx (code, ARMREG_IP1, ARMREG_IP0, MONO_STRUCT_OFFSET (SeqPointInfo, ss_tramp_addr));
+		code = emit_strx (code, ARMREG_IP1, ins->inst_basereg, ins->inst_offset);
+	} else {
+		MonoInst *ins;
+
+		if (cfg->arch.ss_tramp_var) {
+			/* Initialize ss_tramp_var */
+			ins = cfg->arch.ss_tramp_var;
+			g_assert (ins->opcode == OP_REGOFFSET);
+
+			code = emit_imm64 (code, ARMREG_IP0, (guint64)&ss_trampoline);
+			code = emit_strx (code, ARMREG_IP0, ins->inst_basereg, ins->inst_offset);
+		}
+
+		if (cfg->arch.bp_tramp_var) {
+			/* Initialize bp_tramp_var */
+			ins = cfg->arch.bp_tramp_var;
+			g_assert (ins->opcode == OP_REGOFFSET);
+
+			code = emit_imm64 (code, ARMREG_IP0, (guint64)bp_trampoline);
+			code = emit_strx (code, ARMREG_IP0, ins->inst_basereg, ins->inst_offset);
+		}
+	}
+
+	max_offset = 0;
+	if (cfg->opt & MONO_OPT_BRANCH) {
+		for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+			MonoInst *ins;
+			bb->max_offset = max_offset;
+
+			MONO_BB_FOR_EACH_INS (bb, ins) {
+				max_offset += ((guint8 *)ins_get_spec (ins->opcode))[MONO_INST_LEN];
+			}
+		}
+	}
+	if (max_offset > 0x3ffff * 4)
+		cfg->arch.cond_branch_islands = TRUE;
+
+	return code;
+}
+
+static guint8*
+realloc_code (MonoCompile *cfg, int size)
+{
+	while (cfg->code_len + size > (cfg->code_size - 16)) {
+		cfg->code_size *= 2;
+		cfg->native_code = g_realloc (cfg->native_code, cfg->code_size);
+		cfg->stat_code_reallocs++;
+	}
+	return cfg->native_code + cfg->code_len;
+}
+
+void
+mono_arch_emit_epilog (MonoCompile *cfg)
+{
+	CallInfo *cinfo;
+	int max_epilog_size;
+	guint8 *code;
+	int i;
+
+	max_epilog_size = 16 + 20*4;
+	code = realloc_code (cfg, max_epilog_size);
+
+	if (cfg->method->save_lmf) {
+		code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS & cfg->used_int_regs, ARMREG_FP, cfg->lmf_var->inst_offset + MONO_STRUCT_OFFSET (MonoLMF, gregs) - (MONO_ARCH_FIRST_LMF_REG * 8));
+	} else {
+		/* Restore gregs */
+		code = emit_load_regset (code, MONO_ARCH_CALLEE_SAVED_REGS & cfg->used_int_regs, ARMREG_FP, cfg->arch.saved_gregs_offset);
+	}
+
+	/* Load returned vtypes into registers if needed */
+	cinfo = cfg->arch.cinfo;
+	switch (cinfo->ret.storage) {
+	case ArgVtypeInIRegs: {
+		MonoInst *ins = cfg->ret;
+
+		for (i = 0; i < cinfo->ret.nregs; ++i)
+			code = emit_ldrx (code, cinfo->ret.reg + i, ins->inst_basereg, ins->inst_offset + (i * 8));
+		break;
+	}
+	case ArgHFA: {
+		MonoInst *ins = cfg->ret;
+
+		for (i = 0; i < cinfo->ret.nregs; ++i) {
+			if (cinfo->ret.esize == 4)
+				code = emit_ldrfpw (code, cinfo->ret.reg + i, ins->inst_basereg, ins->inst_offset + cinfo->ret.foffsets [i]);
+			else
+				code = emit_ldrfpx (code, cinfo->ret.reg + i, ins->inst_basereg, ins->inst_offset + cinfo->ret.foffsets [i]);
+		}
+		break;
+	}
+	default:
+		break;
+	}
+
+	/* Destroy frame */
+	code = mono_arm_emit_destroy_frame (code, cfg->stack_offset, ((1 << ARMREG_IP0) | (1 << ARMREG_IP1)));
+
+	arm_retx (code, ARMREG_LR);
+
+	g_assert (code - (cfg->native_code + cfg->code_len) < max_epilog_size);
+
+	cfg->code_len = code - cfg->native_code;
+}
+
+void
+mono_arch_emit_exceptions (MonoCompile *cfg)
+{
+	MonoJumpInfo *ji;
+	MonoClass *exc_class;
+	guint8 *code, *ip;
+	guint8* exc_throw_pos [MONO_EXC_INTRINS_NUM];
+	guint8 exc_throw_found [MONO_EXC_INTRINS_NUM];
+	int i, id, size = 0;
+
+	for (i = 0; i < MONO_EXC_INTRINS_NUM; i++) {
+		exc_throw_pos [i] = NULL;
+		exc_throw_found [i] = 0;
+	}
+
+	for (ji = cfg->patch_info; ji; ji = ji->next) {
+		if (ji->type == MONO_PATCH_INFO_EXC) {
+			i = mini_exception_id_by_name (ji->data.target);
+			if (!exc_throw_found [i]) {
+				size += 32;
+				exc_throw_found [i] = TRUE;
+			}
+		}
+	}
+
+	code = realloc_code (cfg, size);
+
+	/* Emit code to raise corlib exceptions */
+	for (ji = cfg->patch_info; ji; ji = ji->next) {
+		if (ji->type != MONO_PATCH_INFO_EXC)
+			continue;
+
+		ip = cfg->native_code + ji->ip.i;
+
+		id = mini_exception_id_by_name (ji->data.target);
+
+		if (exc_throw_pos [id]) {
+			/* ip points to the bcc () in OP_COND_EXC_... */
+			arm_patch_rel (ip, exc_throw_pos [id], ji->relocation);
+			ji->type = MONO_PATCH_INFO_NONE;
+			continue;
+		}
+
+		exc_throw_pos [id] = code;
+		arm_patch_rel (ip, code, ji->relocation);
+
+		/* We are being branched to from the code generated by emit_cond_exc (), the pc is in ip1 */
+
+		/* r0 = type token */
+		exc_class = mono_class_from_name (mono_defaults.corlib, "System", ji->data.name);
+		g_assert (exc_class);
+		code = emit_imm (code, ARMREG_R0, exc_class->type_token - MONO_TOKEN_TYPE_DEF);
+		/* r1 = throw ip */
+		arm_movx (code, ARMREG_R1, ARMREG_IP1);
+		/* Branch to the corlib exception throwing trampoline */
+		ji->ip.i = code - cfg->native_code;
+		ji->type = MONO_PATCH_INFO_INTERNAL_METHOD;
+		ji->data.name = "mono_arch_throw_corlib_exception";
+		ji->relocation = MONO_R_ARM64_BL;
+		arm_bl (code, 0);
+		cfg->thunk_area += THUNK_SIZE;
+	}
+
+	cfg->code_len = code - cfg->native_code;
+
+	g_assert (cfg->code_len < cfg->code_size);
+}
+
+MonoInst*
+mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
+{
+	return NULL;
+}
+
+gboolean
+mono_arch_print_tree (MonoInst *tree, int arity)
+{
+	return FALSE;
+}
+
+guint32
+mono_arch_get_patch_offset (guint8 *code)
+{
+	return 0;
+}
+
+gpointer
+mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
+						   gpointer fail_tramp)
+{
+	int i, buf_len, imt_reg;
+	guint8 *buf, *code;
+
+#if DEBUG_IMT
+	printf ("building IMT thunk for class %s %s entries %d code size %d code at %p end %p vtable %p\n", vtable->klass->name_space, vtable->klass->name, count, size, start, ((guint8*)start) + size, vtable);
+	for (i = 0; i < count; ++i) {
+		MonoIMTCheckItem *item = imt_entries [i];
+		printf ("method %d (%p) %s vtable slot %p is_equals %d chunk size %d\n", i, item->key, item->key->name, &vtable->vtable [item->value.vtable_slot], item->is_equals, item->chunk_size);
+	}
+#endif
+
+	buf_len = 0;
+	for (i = 0; i < count; ++i) {
+		MonoIMTCheckItem *item = imt_entries [i];
+		if (item->is_equals) {
+			gboolean fail_case = !item->check_target_idx && fail_tramp;
+
+			if (item->check_target_idx || fail_case) {
+				if (!item->compare_done || fail_case) {
+					buf_len += 4 * 4 + 4;
+				}
+				buf_len += 4;
+				if (item->has_target_code) {
+					buf_len += 5 * 4;
+				} else {
+					buf_len += 6 * 4;
+				}
+				if (fail_case) {
+					buf_len += 5 * 4;
+				}
+			} else {
+				buf_len += 6 * 4;
+			}
+		} else {
+			buf_len += 6 * 4;
+		}
+	}
+
+	if (fail_tramp)
+		buf = mono_method_alloc_generic_virtual_thunk (domain, buf_len);
+	else
+		buf = mono_domain_code_reserve (domain, buf_len);
+	code = buf;
+
+	/*
+	 * We are called by JITted code, which passes in the IMT argument in
+	 * MONO_ARCH_RGCTX_REG (r27). We need to preserve all caller saved regs
+	 * except ip0/ip1.
+	 */
+	imt_reg = MONO_ARCH_RGCTX_REG;
+	for (i = 0; i < count; ++i) {
+		MonoIMTCheckItem *item = imt_entries [i];
+
+		item->code_target = code;
+
+		if (item->is_equals) {
+			/*
+			 * Check the imt argument against item->key, if equals, jump to either
+			 * item->value.target_code or to vtable [item->value.vtable_slot].
+			 * If fail_tramp is set, jump to it if not-equals.
+			 */
+			gboolean fail_case = !item->check_target_idx && fail_tramp;
+
+			if (item->check_target_idx || fail_case) {
+				/* Compare imt_reg with item->key */
+				if (!item->compare_done || fail_case) {
+					// FIXME: Optimize this
+					code = emit_imm64 (code, ARMREG_IP0, (guint64)item->key);
+					arm_cmpx (code, imt_reg, ARMREG_IP0);
+				}
+				item->jmp_code = code;
+				arm_bcc (code, ARMCOND_NE, 0);
+				/* Jump to target if equals */
+				if (item->has_target_code) {
+					code = emit_imm64 (code, ARMREG_IP0, (guint64)item->value.target_code);
+					arm_brx (code, ARMREG_IP0);
+				} else {
+					guint64 imm = (guint64)&(vtable->vtable [item->value.vtable_slot]);
+
+					code = emit_imm64 (code, ARMREG_IP0, imm);
+					arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 0);
+					arm_brx (code, ARMREG_IP0);
+				}
+
+				if (fail_case) {
+					arm_patch_rel (item->jmp_code, code, MONO_R_ARM64_BCC);
+					item->jmp_code = NULL;
+					code = emit_imm64 (code, ARMREG_IP0, (guint64)fail_tramp);
+					arm_brx (code, ARMREG_IP0);
+				}
+			} else {
+				guint64 imm = (guint64)&(vtable->vtable [item->value.vtable_slot]);
+
+				code = emit_imm64 (code, ARMREG_IP0, imm);
+				arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, 0);
+				arm_brx (code, ARMREG_IP0);
+			}
+		} else {
+			code = emit_imm64 (code, ARMREG_IP0, (guint64)item->key);
+			arm_cmpx (code, imt_reg, ARMREG_IP0);
+			item->jmp_code = code;
+			arm_bcc (code, ARMCOND_HS, 0);
+		}
+	}
+	/* Patch the branches */
+	for (i = 0; i < count; ++i) {
+		MonoIMTCheckItem *item = imt_entries [i];
+		if (item->jmp_code && item->check_target_idx)
+			arm_patch_rel (item->jmp_code, imt_entries [item->check_target_idx]->code_target, MONO_R_ARM64_BCC);
+	}
+
+	g_assert ((code - buf) < buf_len);
+
+	mono_arch_flush_icache (buf, code - buf);
+
+	return buf;
+}
+
+GSList *
+mono_arch_get_trampolines (gboolean aot)
+{
+	return mono_arm_get_exception_trampolines (aot);
+}
+
+#else /* DISABLE_JIT */
+
+gpointer
+mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
+						   gpointer fail_tramp)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+#endif /* !DISABLE_JIT */
+
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+
+void
+mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+	guint8 *code = ip;
+	guint32 native_offset = ip - (guint8*)ji->code_start;
+
+	if (ji->from_aot) {
+		SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
+
+		g_assert (native_offset % 4 == 0);
+		g_assert (info->bp_addrs [native_offset / 4] == 0);
+		info->bp_addrs [native_offset / 4] = mini_get_breakpoint_trampoline ();
+	} else {
+		/* ip points to an ldrx */
+		code += 4;
+		arm_blrx (code, ARMREG_IP0);
+		mono_arch_flush_icache (ip, code - ip);
+	}
+}
+
+void
+mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+	guint8 *code = ip;
+
+	if (ji->from_aot) {
+		guint32 native_offset = ip - (guint8*)ji->code_start;
+		SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
+
+		g_assert (native_offset % 4 == 0);
+		info->bp_addrs [native_offset / 4] = NULL;
+	} else {
+		/* ip points to an ldrx */
+		code += 4;
+		arm_nop (code);
+		mono_arch_flush_icache (ip, code - ip);
+	}
+}
+
+void
+mono_arch_start_single_stepping (void)
+{
+	ss_trampoline = mini_get_single_step_trampoline ();
+}
+
+void
+mono_arch_stop_single_stepping (void)
+{
+	ss_trampoline = NULL;
+}
+
+gboolean
+mono_arch_is_single_step_event (void *info, void *sigctx)
+{
+	/* We use soft breakpoints on arm64 */
+	return FALSE;
+}
+
+gboolean
+mono_arch_is_breakpoint_event (void *info, void *sigctx)
+{
+	/* We use soft breakpoints on arm64 */
+	return FALSE;
+}
+
+void
+mono_arch_skip_breakpoint (MonoContext *ctx, MonoJitInfo *ji)
+{
+	g_assert_not_reached ();
+}
+
+void
+mono_arch_skip_single_step (MonoContext *ctx)
+{
+	g_assert_not_reached ();
+}
+
+gpointer
+mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
+{
+	SeqPointInfo *info;
+	MonoJitInfo *ji;
+
+	// FIXME: Add a free function
+
+	mono_domain_lock (domain);
+	info = g_hash_table_lookup (domain_jit_info (domain)->arch_seq_points, 
+								code);
+	mono_domain_unlock (domain);
+
+	if (!info) {
+		ji = mono_jit_info_table_find (domain, (char*)code);
+		g_assert (ji);
+
+		info = g_malloc0 (sizeof (SeqPointInfo) + (ji->code_size / 4) * sizeof(guint8*));
+
+		info->ss_tramp_addr = &ss_trampoline;
+
+		mono_domain_lock (domain);
+		g_hash_table_insert (domain_jit_info (domain)->arch_seq_points,
+							 code, info);
+		mono_domain_unlock (domain);
+	}
+
+	return info;
+}
+
+void
+mono_arch_init_lmf_ext (MonoLMFExt *ext, gpointer prev_lmf)
+{
+	ext->lmf.previous_lmf = prev_lmf;
+	/* Mark that this is a MonoLMFExt */
+	ext->lmf.previous_lmf = (gpointer)(((gssize)ext->lmf.previous_lmf) | 2);
+	ext->lmf.gregs [MONO_ARCH_LMF_REG_SP] = (gssize)ext;
+}
+
+#endif /* MONO_ARCH_SOFT_DEBUG_SUPPORTED */
+
+gboolean
+mono_arch_opcode_supported (int opcode)
+{
+	switch (opcode) {
+	case OP_ATOMIC_ADD_I4:
+	case OP_ATOMIC_ADD_I8:
+	case OP_ATOMIC_EXCHANGE_I4:
+	case OP_ATOMIC_EXCHANGE_I8:
+	case OP_ATOMIC_CAS_I4:
+	case OP_ATOMIC_CAS_I8:
+	case OP_ATOMIC_LOAD_I1:
+	case OP_ATOMIC_LOAD_I2:
+	case OP_ATOMIC_LOAD_I4:
+	case OP_ATOMIC_LOAD_I8:
+	case OP_ATOMIC_LOAD_U1:
+	case OP_ATOMIC_LOAD_U2:
+	case OP_ATOMIC_LOAD_U4:
+	case OP_ATOMIC_LOAD_U8:
+	case OP_ATOMIC_LOAD_R4:
+	case OP_ATOMIC_LOAD_R8:
+	case OP_ATOMIC_STORE_I1:
+	case OP_ATOMIC_STORE_I2:
+	case OP_ATOMIC_STORE_I4:
+	case OP_ATOMIC_STORE_I8:
+	case OP_ATOMIC_STORE_U1:
+	case OP_ATOMIC_STORE_U2:
+	case OP_ATOMIC_STORE_U4:
+	case OP_ATOMIC_STORE_U8:
+	case OP_ATOMIC_STORE_R4:
+	case OP_ATOMIC_STORE_R8:
+		return TRUE;
+	default:
+		return FALSE;
+	}
+}
+
+CallInfo*
+mono_arch_get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSignature *sig)
+{
+	return get_call_info (gsctx, mp, sig);
+}
+
+gpointer
+mono_arch_install_handler_block_guard (MonoJitInfo *ji, MonoJitExceptionInfo *clause, MonoContext *ctx, gpointer new_value)
+{
+	gpointer *lr_loc;
+	char *old_value;
+	char *bp;
+
+	/*Load the spvar*/
+	bp = MONO_CONTEXT_GET_BP (ctx);
+	lr_loc = (gpointer*)(bp + clause->exvar_offset);
+
+	old_value = *lr_loc;
+	if ((char*)old_value < (char*)ji->code_start || (char*)old_value > ((char*)ji->code_start + ji->code_size))
+		return old_value;
+
+	*lr_loc = new_value;
+
+	return old_value;
+}
--- mono-4.2.1.102+dfsg2.orig/mono/mini/mini-arm64.h
+++ mono-4.2.1.102+dfsg2/mono/mini/mini-arm64.h
@@ -1 +1,253 @@
-#include "../../../mono-extensions/mono/mini/mini-arm64.h"
+/*
+ * mini-arm64.h
+ *
+ * Copyright 2013 Xamarin Inc
+ *
+ * Based on mini-arm.h:
+ *
+ * Copyright 2011 Xamarin Inc
+ */
+
+#ifndef __MONO_MINI_ARM64_H__
+#define __MONO_MINI_ARM64_H__
+
+#include <mono/arch/arm64/arm64-codegen.h>
+
+#define MONO_ARCH_CPU_SPEC mono_arm64_cpu_desc
+
+#define MONO_MAX_IREGS 32
+#define MONO_MAX_FREGS 32
+
+#define MONO_CONTEXT_SET_LLVM_EXC_REG(ctx, exc) do { (ctx)->regs [0] = (gsize)exc; } while (0)
+
+#define MONO_INIT_CONTEXT_FROM_FUNC(ctx,func) do {	\
+		MONO_CONTEXT_SET_BP ((ctx), __builtin_frame_address (0));	\
+		MONO_CONTEXT_SET_SP ((ctx), __builtin_frame_address (0));	\
+		MONO_CONTEXT_SET_IP ((ctx), (func));	\
+	} while (0)
+
+#define MONO_ARCH_INIT_TOP_LMF_ENTRY(lmf)
+
+/* Parameters used by the register allocator */
+/* r0..r7, r9..r14 (r15 is the imt/rgctx reg) */
+#define MONO_ARCH_CALLEE_REGS 0xfeff
+/* r19..r28 */
+#define MONO_ARCH_CALLEE_SAVED_REGS (0x3ff << 19)
+
+/* v15/v16 is reserved for a scratch reg */
+#define MONO_ARCH_CALLEE_FREGS 0xfffc00ff
+/* v8..v15 */
+#define MONO_ARCH_CALLEE_SAVED_FREGS 0xff00
+
+#define MONO_ARCH_USE_FPSTACK FALSE
+#define MONO_ARCH_FPSTACK_SIZE 0
+
+#define MONO_ARCH_INST_SREG2_MASK(ins) (0)
+
+#define MONO_ARCH_INST_FIXED_REG(desc) ((desc) == 'a' ? ARMREG_R0 : -1)
+
+#define MONO_ARCH_INST_IS_REGPAIR(desc) (0)
+
+#define MONO_ARCH_INST_IS_FLOAT(desc) ((desc) == 'f')
+
+#define MONO_ARCH_INST_REGPAIR_REG2(desc,hreg1) (-1)
+
+#define MONO_ARCH_USE_FPSTACK FALSE
+
+#define MONO_ARCH_FRAME_ALIGNMENT 16
+
+#define MONO_ARCH_CODE_ALIGNMENT 32
+
+/* callee saved regs + fp + sp */
+#define MONO_ARCH_LMF_REGS ((0x3ff << 19) | (1 << ARMREG_FP) | (1 << ARMREG_SP))
+#define MONO_ARCH_NUM_LMF_REGS (10 + 2)
+#define MONO_ARCH_FIRST_LMF_REG ARMREG_R19
+#define MONO_ARCH_LMF_REG_FP 10
+#define MONO_ARCH_LMF_REG_SP 11
+
+struct MonoLMF {
+	/* 
+	 * If the second lowest bit is set to 1, then this is a MonoLMFExt structure, and
+	 * the other fields are not valid.
+	 */
+	gpointer    previous_lmf;
+	gpointer    lmf_addr;
+	mgreg_t    pc;
+	mgreg_t    gregs [MONO_ARCH_NUM_LMF_REGS];
+};
+
+/* Structure used by the sequence points in AOTed code */
+typedef struct {
+	gpointer ss_trigger_page;
+	gpointer bp_trigger_page;
+	gpointer ss_tramp_addr;
+	guint8* bp_addrs [MONO_ZERO_LEN_ARRAY];
+} SeqPointInfo;
+
+#define PARAM_REGS 8
+#define FP_PARAM_REGS 8
+
+#define DYN_CALL_STACK_ARGS 6
+
+typedef struct {
+	/* The +1 is for r8 */
+	mgreg_t regs [PARAM_REGS + 1 + DYN_CALL_STACK_ARGS];
+	mgreg_t res, res2;
+	guint8 *ret;
+	double fpregs [FP_PARAM_REGS];
+	int n_fpargs, n_fpret;
+	guint8 buffer [256];
+} DynCallArgs;
+
+typedef struct {
+	gpointer cinfo;
+	int saved_gregs_offset;
+	/* Points to arguments received on the stack */
+	int args_reg;
+	gboolean cond_branch_islands;
+	gpointer vret_addr_loc;
+	gpointer seq_point_info_var;
+	gpointer ss_tramp_var;
+	gpointer bp_tramp_var;
+	guint8 *thunks;
+	int thunks_size;
+} MonoCompileArch;
+
+#define MONO_ARCH_EMULATE_FREM 1
+#define MONO_ARCH_NO_EMULATE_LONG_MUL_OPTS 1
+#define MONO_ARCH_EMULATE_LONG_MUL_OVF_OPTS 1
+#define MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS 1
+#define MONO_ARCH_NEED_DIV_CHECK 1
+#define MONO_ARCH_EMULATE_MUL_OVF 1
+#define MONO_ARCH_HAVE_IMT 1
+#define MONO_ARCH_HAVE_OP_TAIL_CALL 1
+#define MONO_ARCH_THIS_AS_FIRST_ARG 1
+#define MONO_ARCH_RGCTX_REG ARMREG_R15
+#define MONO_ARCH_IMT_REG MONO_ARCH_RGCTX_REG
+#define MONO_ARCH_VTABLE_REG ARMREG_R0
+#define MONO_ARCH_EXC_REG ARMREG_R0
+#define MONO_ARCH_HAVE_XP_UNWIND 1
+#define MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE 1
+#define MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK 1
+#define MONO_ARCH_USE_SIGACTION 1
+#define MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX 1
+#define MONO_ARCH_HAVE_CONTEXT_SET_INT_REG 1
+#define MONO_ARCH_GSHARED_SUPPORTED 1
+#define MONO_ARCH_AOT_SUPPORTED 1
+#define MONO_ARCH_LLVM_SUPPORTED 1
+#define MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES 1
+#define MONO_ARCH_HAVE_EXCEPTIONS_INIT 1
+#define MONO_ARCH_HAVE_GET_TRAMPOLINES 1
+#define MONO_ARCH_DYN_CALL_SUPPORTED 1
+#define MONO_ARCH_DYN_CALL_PARAM_AREA (DYN_CALL_STACK_ARGS * 8)
+#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
+#define MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX 1
+#define MONO_ARCH_HAVE_GENERAL_RGCTX_LAZY_FETCH_TRAMPOLINE 1
+#ifndef MONO_CROSS_COMPILE
+#define MONO_ARCH_ENABLE_MONO_LMF_VAR 1
+#endif
+#define MONO_ARCH_HAVE_OP_GET_EX_OBJ 1
+#define MONO_ARCH_HAVE_OBJC_GET_SELECTOR 1
+#define MONO_ARCH_HAVE_SDB_TRAMPOLINES 1
+#define MONO_ARCH_HAVE_PATCH_CODE_NEW 1
+#define MONO_ARCH_HAVE_OP_GENERIC_CLASS_INIT 1
+#define MONO_ARCH_HAVE_OPCODE_NEEDS_EMULATION 1
+#define MONO_ARCH_HAVE_DECOMPOSE_LONG_OPTS 1
+#define MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD 1
+
+#ifdef TARGET_IOS
+
+#define MONO_ARCH_REDZONE_SIZE 128
+
+#else
+
+#define MONO_ARCH_REDZONE_SIZE 0
+#if !defined(__PIC__)
+#define MONO_ARCH_HAVE_TLS_GET 1
+#endif
+#define MONO_ARCH_HAVE_TLS_GET_REG 1
+
+#endif
+
+/* Relocations */
+#define MONO_R_ARM64_B 1
+#define MONO_R_ARM64_BCC 2
+#define MONO_R_ARM64_IMM 3
+#define MONO_R_ARM64_BL 4
+#define MONO_R_ARM64_BL_SHORT 5
+#define MONO_R_ARM64_CBZ 6
+
+typedef enum {
+        ArgInIReg,
+        ArgInFReg,
+        ArgInFRegR4,
+        ArgOnStack,
+        ArgOnStackR8,
+        ArgOnStackR4,
+        /*
+         * Vtype passed in consecutive int registers.
+         * ainfo->reg is the firs register,
+         * ainfo->nregs is the number of registers,
+         * ainfo->size is the size of the structure.
+         */
+        ArgVtypeInIRegs,
+        ArgVtypeByRef,
+        ArgVtypeByRefOnStack,
+        ArgVtypeOnStack,
+        ArgHFA,
+        ArgNone
+} ArgStorage;
+
+typedef struct {
+        ArgStorage storage;
+        int reg;
+        /* ArgOnStack */
+        int offset;
+        /* ArgVtypeInIRegs/ArgHFA */
+        int nregs, size;
+        /* ArgHFA */
+        int esize;
+        /* ArgHFA */
+        /* The offsets of the float values inside the arg */
+        guint16 foffsets [4];
+        /* ArgOnStack */
+        int slot_size;
+        gboolean sign;
+        gboolean gsharedvt;
+        gboolean hfa;
+} ArgInfo;
+
+typedef struct {
+        int nargs;
+        int gr, fr, stack_usage;
+        ArgInfo ret;
+        ArgInfo sig_cookie;
+        ArgInfo args [1];
+} CallInfo;
+
+guint8* mono_arm_emit_imm64 (guint8 *code, int dreg, gint64 imm);
+
+guint8* mono_arm_emit_ldrx (guint8 *code, int rt, int rn, int imm);
+
+guint8* mono_arm_emit_destroy_frame (guint8 *code, int stack_offset, guint64 temp_regs);
+
+guint8* mono_arm_emit_store_regset (guint8 *code, guint64 regs, int basereg, int offset);
+
+guint8* mono_arm_emit_store_regarray (guint8 *code, guint64 regs, int basereg, int offset);
+
+guint8* mono_arm_emit_load_regarray (guint8 *code, guint64 regs, int basereg, int offset);
+
+/* MonoJumpInfo **ji */
+guint8* mono_arm_emit_aotconst (gpointer ji, guint8 *code, guint8 *code_start, int dreg, guint32 patch_type, gconstpointer data);
+
+void mono_arm_patch (guint8 *code, guint8 *target, int relocation);
+
+void mono_arm_throw_exception (gpointer arg, mgreg_t pc, mgreg_t *int_regs, gdouble *fp_regs, gboolean corlib, gboolean rethrow);
+
+GSList* mono_arm_get_exception_trampolines (gboolean aot);
+
+void mono_arm_resume_unwind (gpointer arg, mgreg_t pc, mgreg_t *int_regs, gdouble *fp_regs, gboolean corlib, gboolean rethrow);
+
+CallInfo* mono_arch_get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSignature *sig);
+
+#endif /* __MONO_MINI_ARM64_H__ */
--- mono-4.2.1.102+dfsg2.orig/mono/mini/mini-ppc.c
+++ mono-4.2.1.102+dfsg2/mono/mini/mini-ppc.c
@@ -1091,8 +1091,10 @@ get_call_info (MonoGenericSharingContext
 					cinfo->args [n].reg = fr;
 					fr ++;
 					FP_ALSO_IN_REG (gr ++);
+#if !defined(__mono_ppc64__)
 					if (size == 8)
 						FP_ALSO_IN_REG (gr ++);
+#endif
 					ALWAYS_ON_STACK (stack_size += size);
 				} else {
 					cinfo->args [n].offset = PPC_STACK_PARAM_OFFSET + stack_size;
@@ -1695,15 +1697,27 @@ mono_arch_emit_outarg_vt (MonoCompile *c
 		} else
 #endif
 			for (i = 0; i < ainfo->vtregs; ++i) {
+	 			dreg = mono_alloc_ireg (cfg);
+#if G_BYTE_ORDER == G_BIG_ENDIAN
 				int antipadding = 0;
 				if (ainfo->bytes) {
 					g_assert (i == 0);
 					antipadding = sizeof (gpointer) - ainfo->bytes;
 				}
-				dreg = mono_alloc_ireg (cfg);
 				MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, soffset);
 				if (antipadding)
 					MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_UN_IMM, dreg, dreg, antipadding * 8);
+#else
+				if (ainfo->bytes && mono_class_native_size (ins->klass, NULL) == 1) {
+					MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, dreg, src->dreg, soffset);
+				} else if (ainfo->bytes && mono_class_native_size (ins->klass, NULL) == 2) {
+					MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, dreg, src->dreg, soffset);
+				} else if (ainfo->bytes && mono_class_native_size (ins->klass, NULL) == 4) { // WDS -- Maybe <= 4?
+					MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU4_MEMBASE, dreg, src->dreg, soffset);
+				} else {
+					MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, soffset);
+				}
+#endif
 				mono_call_inst_add_outarg_reg (cfg, call, dreg, ainfo->reg + i, FALSE);
 				soffset += sizeof (gpointer);
 			}
@@ -2242,8 +2256,11 @@ mono_arch_decompose_opts (MonoCompile *c
 		else
 			MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, ins->dreg, result_shifted_reg, 32);
 		ins->opcode = OP_NOP;
+		break;
 	}
 #endif
+	default:
+		break;
 	}
 }
 
@@ -3735,6 +3752,14 @@ mono_arch_output_basic_block (MonoCompil
 			if (ins->dreg != ins->sreg1)
 				ppc_fmr (code, ins->dreg, ins->sreg1);
 			break;
+		case OP_MOVE_F_TO_I4:
+			ppc_stfs (code, ins->sreg1, -4, ppc_r1);
+			ppc_ldptr (code, ins->dreg, -4, ppc_r1);
+			break;
+		case OP_MOVE_I4_TO_F:
+			ppc_stw (code, ins->sreg1, -4, ppc_r1);
+			ppc_lfs (code, ins->dreg, -4, ppc_r1);
+			break;
 		case OP_FCONV_TO_R4:
 			ppc_frsp (code, ins->dreg, ins->sreg1);
 			break;
@@ -4417,6 +4442,22 @@ mono_arch_output_basic_block (MonoCompil
 			else
 				ppc_mr (code, ins->dreg, ins->sreg1);
 			break;
+#else
+		case OP_ICONV_TO_R4:
+		case OP_ICONV_TO_R8: {
+			if (cpu_hw_caps & PPC_ISA_64) {
+				ppc_srawi(code, ppc_r0, ins->sreg1, 31);
+				ppc_stw (code, ppc_r0, -8, ppc_r1);
+				ppc_stw (code, ins->sreg1, -4, ppc_r1);
+				ppc_lfd (code, ins->dreg, -8, ppc_r1);
+				ppc_fcfid (code, ins->dreg, ins->dreg);
+				if (ins->opcode == OP_ICONV_TO_R4)
+					ppc_frsp (code, ins->dreg, ins->dreg);
+				}
+			break;
+		}
+#endif
+
 		case OP_ATOMIC_ADD_I4:
 		CASE_PPC64 (OP_ATOMIC_ADD_I8) {
 			int location = ins->inst_basereg;
@@ -4450,21 +4491,6 @@ mono_arch_output_basic_block (MonoCompil
 			ppc_mr (code, ins->dreg, ppc_r0);
 			break;
 		}
-#else
-		case OP_ICONV_TO_R4:
-		case OP_ICONV_TO_R8: {
-			if (cpu_hw_caps & PPC_ISA_64) {
-				ppc_srawi(code, ppc_r0, ins->sreg1, 31);
-				ppc_stw (code, ppc_r0, -8, ppc_r1);
-				ppc_stw (code, ins->sreg1, -4, ppc_r1);
-				ppc_lfd (code, ins->dreg, -8, ppc_r1);
-				ppc_fcfid (code, ins->dreg, ins->dreg);
-				if (ins->opcode == OP_ICONV_TO_R4)
-					ppc_frsp (code, ins->dreg, ins->dreg);
-				}
-			break;
-		}
-#endif
 		case OP_ATOMIC_CAS_I4:
 		CASE_PPC64 (OP_ATOMIC_CAS_I8) {
 			int location = ins->sreg1;
@@ -5040,9 +5066,21 @@ mono_arch_emit_prolog (MonoCompile *cfg)
 #ifdef __mono_ppc64__
 						if (ainfo->bytes) {
 							g_assert (cur_reg == 0);
+#if G_BYTE_ORDER == G_BIG_ENDIAN
 							ppc_sldi (code, ppc_r0, ainfo->reg,
 									(sizeof (gpointer) - ainfo->bytes) * 8);
 							ppc_stptr (code, ppc_r0, doffset, inst->inst_basereg);
+#else
+							if (mono_class_native_size (inst->klass, NULL) == 1) {
+							  ppc_stb (code, ainfo->reg + cur_reg, doffset, inst->inst_basereg);
+							} else if (mono_class_native_size (inst->klass, NULL) == 2) {
+								ppc_sth (code, ainfo->reg + cur_reg, doffset, inst->inst_basereg);
+							} else if (mono_class_native_size (inst->klass, NULL) == 4) {  // WDS -- maybe <=4?
+								ppc_stw (code, ainfo->reg + cur_reg, doffset, inst->inst_basereg);
+							} else {
+								ppc_stptr (code, ainfo->reg + cur_reg, doffset, inst->inst_basereg);  // WDS -- Better way?
+							}
+#endif
 						} else
 #endif
 						{
--- mono-4.2.1.102+dfsg2.orig/mono/mini/mini-trampolines.c
+++ mono-4.2.1.102+dfsg2/mono/mini/mini-trampolines.c
@@ -1066,7 +1066,7 @@ mono_delegate_trampoline (mgreg_t *regs,
 				}
 			}
 		}
-	} else {
+	} else if (delegate->method_ptr) {
 		ji = mono_jit_info_table_find (domain, mono_get_addr_from_ftnptr (delegate->method_ptr));
 		if (ji)
 			method = jinfo_get_method (ji);
--- mono-4.2.1.102+dfsg2.orig/mono/mini/tramp-arm64.c
+++ mono-4.2.1.102+dfsg2/mono/mini/tramp-arm64.c
@@ -1 +1,705 @@
-#include "../../../mono-extensions/mono/mini/tramp-arm64.c"
+/*
+ * tramp-arm64.c: JIT trampoline code for ARM64
+ *
+ * Copyright 2013 Xamarin Inc
+ *
+ * Based on tramp-arm.c:
+ * 
+ * Authors:
+ *   Paolo Molaro (lupus@ximian.com)
+ *
+ * (C) 2001-2003 Ximian, Inc.
+ * Copyright 2003-2011 Novell Inc
+ * Copyright 2011 Xamarin Inc
+ */
+
+#include "mini.h"
+#include "debugger-agent.h"
+
+#include <mono/arch/arm64/arm64-codegen.h>
+#include <mono/metadata/abi-details.h>
+
+#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
+
+void
+mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr)
+{
+	mono_arm_patch (code_ptr - 4, addr, MONO_R_ARM64_BL);
+	mono_arch_flush_icache (code_ptr - 4, 4);
+}
+
+void
+mono_arch_patch_plt_entry (guint8 *code, gpointer *got, mgreg_t *regs, guint8 *addr)
+{
+	guint32 ins;
+	guint64 slot_addr;
+	int disp;
+
+	/* 
+	 * Decode the address loaded by the PLT entry emitted by arch_emit_plt_entry () in
+	 * aot-compiler.c
+	 */
+
+	/* adrp */
+	ins = ((guint32*)code) [0];
+	g_assert (((ins >> 24) & 0x1f) == 0x10);
+	disp = (((ins >> 5) & 0x7ffff) << 2) | ((ins >> 29) & 0x3);
+	/* FIXME: disp is signed */
+	g_assert ((disp >> 20) == 0);
+
+	slot_addr = ((guint64)code + (disp << 12)) & ~0xfff;
+
+	/* add x16, x16, :lo12:got */
+	ins = ((guint32*)code) [1];
+	g_assert (((ins >> 22) & 0x3) == 0);
+	slot_addr += (ins >> 10) & 0xfff;
+
+	/* ldr x16, [x16, <offset>] */
+	ins = ((guint32*)code) [2];
+	g_assert (((ins >> 24) & 0x3f) == 0x39);
+	slot_addr += ((ins >> 10) & 0xfff) * 8;
+
+	g_assert (*(guint64*)slot_addr);
+	*(gpointer*)slot_addr = addr;
+}
+
+void
+mono_arch_nullify_class_init_trampoline (guint8 *code, mgreg_t *regs)
+{
+	mono_arch_patch_callsite (NULL, code, mini_get_nullified_class_init_trampoline ());
+}
+
+guint8*
+mono_arch_get_call_target (guint8 *code)
+{
+	guint32 imm;
+	int disp;
+
+	code -= 4;
+
+	imm = *(guint32*)code;
+	/* Should be a bl */
+	g_assert (((imm >> 31) & 0x1) == 0x1);
+	g_assert (((imm >> 26) & 0x7) == 0x5);
+
+	disp = (imm & 0x3ffffff);
+	if ((disp >> 25) != 0)
+		/* Negative, sing extend to 32 bits */
+		disp = disp | 0xfc000000;
+
+	return code + (disp * 4);
+}
+
+guint32
+mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
+{
+	/* The offset is stored as the 5th word of the plt entry */
+	return ((guint32*)plt_entry) [4];
+}
+
+#ifndef DISABLE_JIT
+
+guchar*
+mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
+{
+	guint8 *code, *buf, *tramp;
+	int i, buf_len, imm;
+	int frame_size, offset, gregs_offset, num_fregs, fregs_offset, arg_offset, lmf_offset;
+	guint64 gregs_regset;
+	GSList *unwind_ops = NULL;
+	MonoJumpInfo *ji = NULL;
+	char *tramp_name;
+
+	buf_len = 768;
+	buf = code = mono_global_codeman_reserve (buf_len);
+
+	/*
+	 * We are getting called by a specific trampoline, ip1 contains the trampoline argument.
+	 */
+
+	/* Compute stack frame size and offsets */
+	offset = 0;
+	/* frame block */
+	offset += 2 * 8;
+	/* gregs */
+	gregs_offset = offset;
+	offset += 32 * 8;
+	/* fregs */
+	// FIXME: Save 128 bits
+	/* Only have to save the argument regs */
+	num_fregs = 8;
+	fregs_offset = offset;
+	offset += num_fregs * 8;
+	/* arg */
+	arg_offset = offset;
+	offset += 8;
+	/* LMF */
+	lmf_offset = offset;
+	offset += sizeof (MonoLMF);
+	//offset += 22 * 8;
+	frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
+
+	/* Setup stack frame */
+	imm = frame_size;
+	while (imm > 256) {
+		arm_subx_imm (code, ARMREG_SP, ARMREG_SP, 256);
+		imm -= 256;
+	}
+	arm_subx_imm (code, ARMREG_SP, ARMREG_SP, imm);
+	arm_stpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0);
+	arm_movspx (code, ARMREG_FP, ARMREG_SP);
+
+	/* Save gregs */
+	// FIXME: Optimize this
+	gregs_regset = ~((1 << ARMREG_FP) | (1 << ARMREG_SP));
+	code = mono_arm_emit_store_regarray (code, gregs_regset, ARMREG_FP, gregs_offset);
+	/* Save fregs */
+	for (i = 0; i < num_fregs; ++i)
+		arm_strfpx (code, i, ARMREG_FP, fregs_offset + (i * 8));
+	/* Save trampoline arg */
+	arm_strx (code, ARMREG_IP1, ARMREG_FP, arg_offset);
+
+	/* Setup LMF */
+	arm_addx_imm (code, ARMREG_IP0, ARMREG_FP, lmf_offset);
+	code = mono_arm_emit_store_regset (code, MONO_ARCH_LMF_REGS, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, gregs));
+	/* Save caller fp */
+	arm_ldrx (code, ARMREG_IP1, ARMREG_FP, 0);
+	arm_strx (code, ARMREG_IP1, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, gregs) + (MONO_ARCH_LMF_REG_FP * 8));
+	/* Save caller sp */
+	arm_movx (code, ARMREG_IP1, ARMREG_FP);
+	imm = frame_size;
+	while (imm > 256) {
+		arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 256);
+		imm -= 256;
+	}
+	arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, imm);
+	arm_strx (code, ARMREG_IP1, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, gregs) + (MONO_ARCH_LMF_REG_SP * 8));
+	/* Save caller pc */
+	if (tramp_type == MONO_TRAMPOLINE_JUMP)
+		arm_movx (code, ARMREG_LR, ARMREG_RZR);
+	else
+		arm_ldrx (code, ARMREG_LR, ARMREG_FP, 8);
+	arm_strx (code, ARMREG_LR, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, pc));
+
+	/* Save LMF */
+	/* Similar to emit_save_lmf () */
+	if (aot) {
+		code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_get_lmf_addr");
+	} else {
+		tramp = (guint8*)mono_get_lmf_addr;
+		code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)tramp);
+	}
+	arm_blrx (code, ARMREG_IP0);
+	/* r0 contains the address of the tls slot holding the current lmf */
+	/* ip0 = lmf */
+	arm_addx_imm (code, ARMREG_IP0, ARMREG_FP, lmf_offset);
+	/* lmf->lmf_addr = lmf_addr */
+	arm_strx (code, ARMREG_R0, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, lmf_addr));
+	/* lmf->previous_lmf = *lmf_addr */
+	arm_ldrx (code, ARMREG_IP1, ARMREG_R0, 0);
+	arm_strx (code, ARMREG_IP1, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
+	/* *lmf_addr = lmf */
+	arm_strx (code, ARMREG_IP0, ARMREG_R0, 0);
+
+	/* Call the C trampoline function */
+	/* Arg 1 = gregs */
+	arm_addx_imm (code, ARMREG_R0, ARMREG_FP, gregs_offset);
+	/* Arg 2 = caller */
+	if (tramp_type == MONO_TRAMPOLINE_JUMP)
+		arm_movx (code, ARMREG_R1, ARMREG_RZR);
+	else
+		arm_ldrx (code, ARMREG_R1, ARMREG_FP, gregs_offset + (ARMREG_LR * 8));
+	/* Arg 3 = arg */
+	if (MONO_TRAMPOLINE_TYPE_HAS_ARG (tramp_type))
+		/* Passed in r0 */
+		arm_ldrx (code, ARMREG_R2, ARMREG_FP, gregs_offset + (ARMREG_R0 * 8));
+	else
+		arm_ldrx (code, ARMREG_R2, ARMREG_FP, arg_offset);
+	/* Arg 4 = trampoline addr */
+	arm_movx (code, ARMREG_R3, ARMREG_RZR);
+
+	if (aot) {
+		char *icall_name = g_strdup_printf ("trampoline_func_%d", tramp_type);
+		code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
+	} else {
+		tramp = (guint8*)mono_get_trampoline_func (tramp_type);
+		code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)tramp);
+	}
+	arm_blrx (code, ARMREG_IP0);
+
+	/* Restore LMF */
+	/* Similar to emit_restore_lmf () */
+	/* Clobbers ip0/ip1 */
+	/* ip0 = lmf */
+	arm_addx_imm (code, ARMREG_IP0, ARMREG_FP, lmf_offset);
+	/* ip1 = lmf->previous_lmf */
+	arm_ldrx (code, ARMREG_IP1, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
+	/* ip0 = lmf->lmf_addr */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoLMF, lmf_addr));
+	/* *lmf_addr = previous_lmf */
+	arm_strx (code, ARMREG_IP1, ARMREG_IP0, 0);
+
+	/* Save the result to ip1 */
+	arm_movx (code, ARMREG_IP1, ARMREG_R0);
+
+	/* Restore gregs */
+	/* Only have to load the argument regs (r0..r8) and the rgctx reg */
+	code = mono_arm_emit_load_regarray (code, 0x1ff | (1 << ARMREG_LR) | (1 << MONO_ARCH_RGCTX_REG), ARMREG_FP, gregs_offset);
+	/* Restore fregs */
+	for (i = 0; i < num_fregs; ++i)
+		arm_ldrfpx (code, i, ARMREG_FP, fregs_offset + (i * 8));
+
+	/* These trampolines return a value */
+	if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
+		arm_movx (code, ARMREG_R0, ARMREG_IP1);
+
+	/* Cleanup frame */
+	code = mono_arm_emit_destroy_frame (code, frame_size, ((1 << ARMREG_IP0)));
+
+	if ((tramp_type == MONO_TRAMPOLINE_CLASS_INIT) || (tramp_type == MONO_TRAMPOLINE_GENERIC_CLASS_INIT) || (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH))
+		arm_retx (code, ARMREG_LR);
+	else
+		arm_brx (code, ARMREG_IP1);
+
+	g_assert ((code - buf) < buf_len);
+	mono_arch_flush_icache (buf, code - buf);
+
+	if (info) {
+		tramp_name = mono_get_generic_trampoline_name (tramp_type);
+		*info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
+		g_free (tramp_name);
+	}
+
+	return buf;
+}
+
+gpointer
+mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
+{
+	guint8 *buf, *code;
+
+	code = buf = mono_global_codeman_reserve (16);
+
+	arm_retx (code, ARMREG_LR);
+
+	mono_arch_flush_icache (buf, code - buf);
+
+	if (info)
+		*info = mono_tramp_info_create ("nullified_class_init_trampoline", buf, code - buf, NULL, NULL);
+
+	return buf;
+}
+
+gpointer
+mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
+{
+	guint8 *code, *buf, *tramp;
+	int buf_len = 64;
+
+	/*
+	 * Return a trampoline which calls generic trampoline TRAMP_TYPE passing in ARG1.
+	 * Pass the argument in ip1, clobbering ip0.
+	 */
+	tramp = mono_get_trampoline_code (tramp_type);
+
+	buf = code = mono_global_codeman_reserve (buf_len);
+
+	code = mono_arm_emit_imm64 (code, ARMREG_IP1, (guint64)arg1);
+	code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)tramp);
+
+	arm_brx (code, ARMREG_IP0);
+
+	g_assert ((code - buf) < buf_len);
+	mono_arch_flush_icache (buf, code - buf);
+	if (code_len)
+		*code_len = code - buf;
+
+	return buf;
+}
+
+gpointer
+mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
+{
+	guint8 *code, *start;
+	guint32 size = 32;
+	MonoDomain *domain = mono_domain_get ();
+
+	start = code = mono_domain_code_reserve (domain, size);
+	code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr);
+	arm_addx_imm (code, ARMREG_R0, ARMREG_R0, sizeof (MonoObject));
+	arm_brx (code, ARMREG_IP0);
+
+	g_assert ((code - start) <= size);
+	mono_arch_flush_icache (start, code - start);
+	return start;
+}
+
+gpointer
+mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
+{
+	guint8 *code, *start;
+	guint32 buf_len = 32;
+	MonoDomain *domain = mono_domain_get ();
+
+	start = code = mono_domain_code_reserve (domain, buf_len);
+	code = mono_arm_emit_imm64 (code, MONO_ARCH_RGCTX_REG, (guint64)mrgctx);
+	code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr);
+	arm_brx (code, ARMREG_IP0);
+
+	g_assert ((code - start) <= buf_len);
+
+	mono_arch_flush_icache (start, code - start);
+
+	return start;
+}
+
+gpointer
+mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
+{
+	guint8 *code, *buf;
+	int buf_size;
+	int i, depth, index, njumps;
+	gboolean is_mrgctx;
+	guint8 **rgctx_null_jumps;
+	MonoJumpInfo *ji = NULL;
+	GSList *unwind_ops = NULL;
+	guint8 *tramp;
+	guint32 code_len;
+
+	is_mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
+	index = MONO_RGCTX_SLOT_INDEX (slot);
+	if (is_mrgctx)
+		index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
+	for (depth = 0; ; ++depth) {
+		int size = mono_class_rgctx_get_array_size (depth, is_mrgctx);
+
+		if (index < size - 1)
+			break;
+		index -= size - 1;
+	}
+
+	buf_size = 64 + 16 * depth;
+	code = buf = mono_global_codeman_reserve (buf_size);
+
+	rgctx_null_jumps = g_malloc0 (sizeof (guint8*) * (depth + 2));
+	njumps = 0;
+
+	/* The vtable/mrgtx is in R0 */
+	g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
+
+	if (is_mrgctx) {
+		/* get mrgctx ptr */
+		arm_movx (code, ARMREG_IP1, ARMREG_R0);
+ 	} else {
+		/* load rgctx ptr from vtable */
+		code = mono_arm_emit_ldrx (code, ARMREG_IP1, ARMREG_R0, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
+		/* is the rgctx ptr null? */
+		/* if yes, jump to actual trampoline */
+		rgctx_null_jumps [njumps ++] = code;
+		arm_cbzx (code, ARMREG_IP1, 0);
+	}
+
+	for (i = 0; i < depth; ++i) {
+		/* load ptr to next array */
+		if (is_mrgctx && i == 0) {
+			code = mono_arm_emit_ldrx (code, ARMREG_IP1, ARMREG_IP1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
+		} else {
+			code = mono_arm_emit_ldrx (code, ARMREG_IP1, ARMREG_IP1, 0);
+		}
+		/* is the ptr null? */
+		/* if yes, jump to actual trampoline */
+		rgctx_null_jumps [njumps ++] = code;
+		arm_cbzx (code, ARMREG_IP1, 0);
+	}
+
+	/* fetch slot */
+	code = mono_arm_emit_ldrx (code, ARMREG_IP1, ARMREG_IP1, sizeof (gpointer) * (index + 1));
+	/* is the slot null? */
+	/* if yes, jump to actual trampoline */
+	rgctx_null_jumps [njumps ++] = code;
+	arm_cbzx (code, ARMREG_IP1, 0);
+	/* otherwise return, result is in IP1 */
+	arm_movx (code, ARMREG_R0, ARMREG_IP1);
+	arm_brx (code, ARMREG_LR);
+
+	g_assert (njumps <= depth + 2);
+	for (i = 0; i < njumps; ++i)
+		mono_arm_patch (rgctx_null_jumps [i], code, MONO_R_ARM64_CBZ);
+
+	g_free (rgctx_null_jumps);
+
+	/* Slowpath */
+
+	/* Call mono_rgctx_lazy_fetch_trampoline (), passing in the slot as argument */
+	/* The vtable/mrgctx is still in R0 */
+	if (aot) {
+		code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, g_strdup_printf ("specific_trampoline_lazy_fetch_%u", slot));
+	} else {
+		tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
+		code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)tramp);
+	}
+	arm_brx (code, ARMREG_IP0);
+
+	mono_arch_flush_icache (buf, code - buf);
+
+	g_assert (code - buf <= buf_size);
+
+	if (info) {
+		char *name = mono_get_rgctx_fetch_trampoline_name (slot);
+		*info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
+		g_free (name);
+	}
+
+	return buf;
+}
+
+gpointer
+mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot)
+{
+	guint8 *code, *buf;
+	int tramp_size;
+	MonoJumpInfo *ji = NULL;
+	GSList *unwind_ops = NULL;
+
+	g_assert (aot);
+
+	tramp_size = 32;
+
+	code = buf = mono_global_codeman_reserve (tramp_size);
+
+	mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, 0);
+
+	// FIXME: Currently, we always go to the slow path.
+	/* Load trampoline addr */
+	arm_ldrx (code, ARMREG_IP0, MONO_ARCH_RGCTX_REG, 8);
+	/* The vtable/mrgctx is in R0 */
+	g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
+	arm_brx (code, ARMREG_IP0);
+
+	mono_arch_flush_icache (buf, code - buf);
+
+	g_assert (code - buf <= tramp_size);
+
+	if (info)
+		*info = mono_tramp_info_create ("rgctx_fetch_trampoline_general", buf, code - buf, ji, unwind_ops);
+
+	return buf;
+}
+
+static gpointer
+handler_block_trampoline_helper (gpointer *ptr)
+{
+	MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id);
+	return jit_tls->handler_block_return_address;
+}
+
+gpointer
+mono_arch_create_handler_block_trampoline (MonoTrampInfo **info, gboolean aot)
+{
+	guint8 *tramp;
+	guint8 *code, *buf;
+	int tramp_size = 64;
+	MonoJumpInfo *ji = NULL;
+	GSList *unwind_ops = NULL;
+
+	g_assert (!aot);
+
+	code = buf = mono_global_codeman_reserve (tramp_size);
+
+	unwind_ops = NULL;
+
+	tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD, NULL, NULL);
+
+	/*
+	This trampoline restore the call chain of the handler block then jumps into the code that deals with it.
+	*/
+
+	/*
+	 * We are in a method frame after the call emitted by OP_CALL_HANDLER.
+	 */
+	code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)handler_block_trampoline_helper);
+	/* Set it as the return address so the trampoline will return to it */
+	arm_movx (code, ARMREG_LR, ARMREG_IP0);
+
+	/* Call the trampoline */
+	code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)tramp);
+	arm_brx (code, ARMREG_IP0);
+
+	mono_arch_flush_icache (buf, code - buf);
+	mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_HELPER, NULL);
+	g_assert (code - buf <= tramp_size);
+
+	*info = mono_tramp_info_create ("handler_block_trampoline", buf, code - buf, ji, unwind_ops);
+
+	return buf;
+}
+
+/*
+ * mono_arch_create_sdb_trampoline:
+ *
+ *   Return a trampoline which captures the current context, passes it to
+ * debugger_agent_single_step_from_context ()/debugger_agent_breakpoint_from_context (),
+ * then restores the (potentially changed) context.
+ */
+guint8*
+mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
+{
+	int tramp_size = 512;
+	int offset, imm, frame_size, ctx_offset;
+	guint64 gregs_regset;
+	guint8 *code, *buf;
+	GSList *unwind_ops = NULL;
+	MonoJumpInfo *ji = NULL;
+
+	code = buf = mono_global_codeman_reserve (tramp_size);
+
+	/* Compute stack frame size and offsets */
+	offset = 0;
+	/* frame block */
+	offset += 2 * 8;
+	/* MonoContext */
+	ctx_offset = offset;
+	offset += sizeof (MonoContext);
+	offset = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);
+	frame_size = offset;
+
+	// FIXME: Unwind info
+
+	/* Setup stack frame */
+	imm = frame_size;
+	while (imm > 256) {
+		arm_subx_imm (code, ARMREG_SP, ARMREG_SP, 256);
+		imm -= 256;
+	}
+	arm_subx_imm (code, ARMREG_SP, ARMREG_SP, imm);
+	arm_stpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0);
+	arm_movspx (code, ARMREG_FP, ARMREG_SP);
+
+	/* Initialize a MonoContext structure on the stack */
+	/* No need to save fregs */
+	gregs_regset = ~((1 << ARMREG_FP) | (1 << ARMREG_SP));
+	code = mono_arm_emit_store_regarray (code, gregs_regset, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, regs));
+	/* Save caller fp */
+	arm_ldrx (code, ARMREG_IP1, ARMREG_FP, 0);
+	arm_strx (code, ARMREG_IP1, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_FP * 8));
+	/* Save caller sp */
+	arm_movx (code, ARMREG_IP1, ARMREG_FP);
+	imm = frame_size;
+	while (imm > 256) {
+		arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 256);
+		imm -= 256;
+	}
+	arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, imm);
+	arm_strx (code, ARMREG_IP1, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_SP * 8));
+	/* Save caller ip */
+	arm_ldrx (code, ARMREG_IP1, ARMREG_FP, 8);
+	arm_strx (code, ARMREG_IP1, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, pc));
+
+	/* Call the single step/breakpoint function in sdb */
+	/* Arg1 = ctx */
+	arm_addx_imm (code, ARMREG_R0, ARMREG_FP, ctx_offset);
+	if (aot) {
+		if (single_step)
+			code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "debugger_agent_single_step_from_context");
+		else
+			code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "debugger_agent_breakpoint_from_context");
+	} else {
+		gpointer addr = single_step ? debugger_agent_single_step_from_context : debugger_agent_breakpoint_from_context;
+
+		code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)addr);
+	}
+	arm_blrx (code, ARMREG_IP0);
+
+	/* Restore ctx */
+	/* Save fp/pc into the frame block */
+	arm_ldrx (code, ARMREG_IP0, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_FP * 8));
+	arm_strx (code, ARMREG_IP0, ARMREG_FP, 0);
+	arm_ldrx (code, ARMREG_IP0, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, pc));
+	arm_strx (code, ARMREG_IP0, ARMREG_FP, 8);
+	gregs_regset = ~((1 << ARMREG_FP) | (1 << ARMREG_SP));
+	code = mono_arm_emit_load_regarray (code, gregs_regset, ARMREG_FP, ctx_offset + G_STRUCT_OFFSET (MonoContext, regs));
+
+	code = mono_arm_emit_destroy_frame (code, frame_size, ((1 << ARMREG_IP0) | (1 << ARMREG_IP1)));
+
+	arm_retx (code, ARMREG_LR);
+
+	mono_arch_flush_icache (code, code - buf);
+	g_assert (code - buf <= tramp_size);
+
+	const char *tramp_name = single_step ? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline";
+	*info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
+
+	return buf;
+}
+
+#else /* DISABLE_JIT */
+
+guchar*
+mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+gpointer
+mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+gpointer
+mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+gpointer
+mono_arch_get_static_rgctx_trampoline (MonoMethod *m, MonoMethodRuntimeGenericContext *mrgctx, gpointer addr)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+gpointer
+mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+gpointer
+mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+guint8*
+mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+#endif /* !DISABLE_JIT */
+
+#if defined(MONO_ARCH_GSHAREDVT_SUPPORTED)
+
+gpointer
+mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
+{
+	if (info)
+		*info = NULL;
+	return NULL;
+}
+
+gpointer
+mono_arch_get_gsharedvt_arg_trampoline (MonoDomain *domain, gpointer arg, gpointer addr)
+{
+	g_assert_not_reached ();
+	return NULL;
+}
+
+#endif /* !MONOTOUCH */
--- mono-4.2.1.102+dfsg2.orig/mono/sgen/sgen-archdep.h
+++ mono-4.2.1.102+dfsg2/mono/sgen/sgen-archdep.h
@@ -89,7 +89,7 @@
 
 /* MS_BLOCK_SIZE must be a multiple of the system pagesize, which for some
    archs is 64k.  */
-#if defined(TARGET_POWERPC64) && _CALL_ELF == 2
+#if defined(TARGET_POWERPC64)
 #define ARCH_MIN_MS_BLOCK_SIZE	(64*1024)
 #define ARCH_MIN_MS_BLOCK_SIZE_SHIFT	16
 #endif
--- mono-4.2.1.102+dfsg2.orig/mono/tests/Makefile.am
+++ mono-4.2.1.102+dfsg2/mono/tests/Makefile.am
@@ -356,7 +356,6 @@ BASE_TEST_CS_SRC=		\
 	modules.cs	\
 	bug-81673.cs	\
 	bug-81691.cs	\
-	bug-80307.cs	\
 	bug-415577.cs	\
 	filter-stack.cs	\
 	vararg2.cs	\
@@ -387,8 +386,6 @@ BASE_TEST_CS_SRC=		\
 	bug-575941.cs	\
 	bug-599469.cs	\
 	bug-389886-3.cs \
-	monitor.cs	\
-	monitor-resurrection.cs	\
 	dynamic-method-resurrection.cs	\
 	bug-666008.cs	\
 	bug-685908.cs	\
@@ -404,7 +401,6 @@ BASE_TEST_CS_SRC=		\
 	appdomain-unload-doesnot-raise-pending-events.cs	\
 	bug-6148.cs	\
 	assembly_append_ordering.cs	\
-	bug-10127.cs	\
 	bug-18026.cs	\
 	allow-synchronous-major.cs	\
 	unload-appdomain-on-shutdown.cs	\
--- mono-4.2.1.102+dfsg2.orig/mono/utils/mono-context.h
+++ mono-4.2.1.102+dfsg2/mono/utils/mono-context.h
@@ -183,11 +183,11 @@ extern void mono_context_get_current (vo
 #define MONO_CONTEXT_GET_CURRENT(ctx)	\
 	__asm__ __volatile__(	\
 		"movq $0x0,  %%nacl:0x00(%%r15, %0, 1)\n"	\
-		"movq %%rbx, %%nacl:0x08(%%r15, %0, 1)\n"	\
-		"movq %%rcx, %%nacl:0x10(%%r15, %0, 1)\n"	\
-		"movq %%rdx, %%nacl:0x18(%%r15, %0, 1)\n"	\
-		"movq %%rbp, %%nacl:0x20(%%r15, %0, 1)\n"	\
-		"movq %%rsp, %%nacl:0x28(%%r15, %0, 1)\n"	\
+		"movq %%rcx, %%nacl:0x08(%%r15, %0, 1)\n"	\
+		"movq %%rdx, %%nacl:0x10(%%r15, %0, 1)\n"	\
+		"movq %%rbx, %%nacl:0x18(%%r15, %0, 1)\n"	\
+		"movq %%rsp, %%nacl:0x20(%%r15, %0, 1)\n"	\
+		"movq %%rbp, %%nacl:0x28(%%r15, %0, 1)\n"	\
 		"movq %%rsi, %%nacl:0x30(%%r15, %0, 1)\n"	\
 		"movq %%rdi, %%nacl:0x38(%%r15, %0, 1)\n"	\
 		"movq %%r8,  %%nacl:0x40(%%r15, %0, 1)\n"	\
@@ -204,14 +204,15 @@ extern void mono_context_get_current (vo
 		: "a" ((int64_t)&(ctx))	\
 		: "rdx", "memory")
 #else
+
 #define MONO_CONTEXT_GET_CURRENT(ctx)	\
 	__asm__ __volatile__(	\
 		"movq $0x0,  0x00(%0)\n"	\
-		"movq %%rbx, 0x08(%0)\n"	\
-		"movq %%rcx, 0x10(%0)\n"	\
-		"movq %%rdx, 0x18(%0)\n"	\
-		"movq %%rbp, 0x20(%0)\n"	\
-		"movq %%rsp, 0x28(%0)\n"	\
+		"movq %%rcx, 0x08(%0)\n"	\
+		"movq %%rdx, 0x10(%0)\n"	\
+		"movq %%rbx, 0x18(%0)\n"	\
+		"movq %%rsp, 0x20(%0)\n"	\
+		"movq %%rbp, 0x28(%0)\n"	\
 		"movq %%rsi, 0x30(%0)\n"	\
 		"movq %%rdi, 0x38(%0)\n"	\
 		"movq %%r8,  0x40(%0)\n"	\
--- mono-4.2.1.102+dfsg2.orig/mono/utils/mono-threads-linux.c
+++ mono-4.2.1.102+dfsg2/mono/utils/mono-threads-linux.c
@@ -1,6 +1,6 @@
 #include <config.h>
 
-#if defined(__linux__) && !defined(PLATFORM_ANDROID)
+#if (defined(__linux__) && !defined(PLATFORM_ANDROID)) || defined(__FreeBSD_kernel__)
 
 #include <mono/utils/mono-threads.h>
 #include <pthread.h>
--- mono-4.2.1.102+dfsg2.orig/support/syslog.c
+++ mono-4.2.1.102+dfsg2/support/syslog.c
@@ -38,7 +38,16 @@ Mono_Posix_Syscall_closelog (void)
 int
 Mono_Posix_Syscall_syslog (int priority, const char* message)
 {
+#ifdef __GNUC__
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Wformat-security"
+#endif
+
 	syslog (priority, message);
+
+#ifdef __GNUC__
+	#pragma GCC diagnostic pop
+#endif
 	return 0;
 }
 
