diff --git a/packages/addons/addon-depends/emby-depends/ffmpegx/package.mk b/packages/addons/addon-depends/emby-depends/ffmpegx/package.mk index 342e24e3c5..f8d58a7724 100644 --- a/packages/addons/addon-depends/emby-depends/ffmpegx/package.mk +++ b/packages/addons/addon-depends/emby-depends/ffmpegx/package.mk @@ -47,6 +47,11 @@ pre_configure_target() { FFMPEG_RPI_HADE="--enable-mmal --enable-omx-rpi" fi +# ffmpeg does not build with libx264 on aarch64 + if [ "$TARGET_ARCH" != "aarch64" ]; then + FFMPEG_X264="--enable-libx264" + fi + if [ "$TARGET_ARCH" == "arm" ]; then FFMPEG_ARM_AO="--enable-hardcoded-tables" fi @@ -66,7 +71,7 @@ configure_target() { \ `#External library support` \ --enable-libmp3lame \ - --enable-libx264 \ + $FFMPEG_X264 \ --enable-openssl \ \ `#Toolchain options` \ diff --git a/packages/addons/service/emby/changelog.txt b/packages/addons/service/emby/changelog.txt index cdce943f80..2da3edea09 100644 --- a/packages/addons/service/emby/changelog.txt +++ b/packages/addons/service/emby/changelog.txt @@ -1,3 +1,8 @@ +8.0.103 +- Updated to version 3.0.6070 +- Build for all architectures +- Refer to libsqlite3.so.0 instead of libsqlite3.so + 8.0.102 - Updated to version 3.0.6030 - Added dedicated ffmpeg and ffprobe diff --git a/packages/addons/service/emby/package.mk b/packages/addons/service/emby/package.mk index c1cacfc143..8d1fd80d14 100644 --- a/packages/addons/service/emby/package.mk +++ b/packages/addons/service/emby/package.mk @@ -17,9 +17,9 @@ ################################################################################ PKG_NAME="emby" -PKG_VERSION="3.0.6030" -PKG_REV="102" -PKG_ARCH="arm x86_64" +PKG_VERSION="3.0.6070" +PKG_REV="103" +PKG_ARCH="any" PKG_LICENSE="OSS" PKG_SITE="http://emby.media" PKG_URL="https://github.com/MediaBrowser/Emby/releases/download/$PKG_VERSION/Emby.Mono.zip" @@ -51,7 +51,11 @@ makeinstall_target() { addon() { mkdir -p $ADDON_BUILD/$PKG_ADDON_ID/Emby.Mono - unzip -q $ROOT/$SOURCES/$PKG_NAME/$PKG_SOURCE_NAME -d $ADDON_BUILD/$PKG_ADDON_ID/Emby.Mono + unzip -q $ROOT/$SOURCES/$PKG_NAME/$PKG_SOURCE_NAME \ + -d $ADDON_BUILD/$PKG_ADDON_ID/Emby.Mono + + sed -i 's/libsqlite3.so/libsqlite3.so.0/g' \ + $ADDON_BUILD/$PKG_ADDON_ID/Emby.Mono/System.Data.SQLite.dll.config mkdir -p $ADDON_BUILD/$PKG_ADDON_ID/bin cp -L $(get_build_dir ffmpegx)/.install_pkg/usr/local/bin/ffmpegx \ diff --git a/packages/addons/service/webgrabplus/package.mk b/packages/addons/service/webgrabplus/package.mk index 71789d77bd..1a0c06d4bd 100644 --- a/packages/addons/service/webgrabplus/package.mk +++ b/packages/addons/service/webgrabplus/package.mk @@ -1,7 +1,7 @@ PKG_NAME="webgrabplus" PKG_VERSION="56.29pre-20160807" PKG_REV="102" -PKG_ARCH="arm x86_64" +PKG_ARCH="any" PKG_LICENSE="prop." PKG_SITE="http://www.webgrabplus.com/" PKG_URL="https://github.com/awiouy/webgrabplus/archive/$PKG_VERSION.tar.gz" diff --git a/packages/addons/tools/mono/changelog.txt b/packages/addons/tools/mono/changelog.txt index 30cf79a734..270f277c1a 100644 --- a/packages/addons/tools/mono/changelog.txt +++ b/packages/addons/tools/mono/changelog.txt @@ -1,2 +1,7 @@ +8.0.101 +- Update to 4.2.1.102 +- Build static for all projects and architectures +- Remove uneeded binaries and libraries + 8.0.100 - Initial release diff --git a/packages/addons/tools/mono/package.mk b/packages/addons/tools/mono/package.mk index 27f295f498..eb003b17f3 100644 --- a/packages/addons/tools/mono/package.mk +++ b/packages/addons/tools/mono/package.mk @@ -17,9 +17,9 @@ ################################################################################ PKG_NAME="mono" -PKG_VERSION="4.0.5.1" -PKG_REV="100" -PKG_ARCH="arm x86_64" +PKG_VERSION="4.2.1.102" +PKG_REV="101" +PKG_ARCH="any" PKG_LICENSE="MIT" PKG_SITE="http://www.mono-project.com" PKG_URL="http://download.mono-project.com/sources/mono/$PKG_NAME-$PKG_VERSION.tar.bz2" @@ -32,69 +32,63 @@ PKG_LONGDESC="Mono ($PKG_VERSION) is a software platform designed to allow devel PKG_AUTORECONF="yes" PKG_IS_ADDON="yes" -PKG_ADDON_NAME="Mono (beta)" +PKG_ADDON_NAME="Mono" PKG_ADDON_TYPE="xbmc.python.script" PKG_ADDON_REPOVERSION="8.0" PKG_MAINTAINER="Anton Voyl (awiouy)" prefix="/storage/.kodi/addons/$PKG_SECTION.$PKG_NAME" -configure_opts="--prefix=$prefix \ - --bindir=$prefix/bin \ - --sysconfdir=$prefix/etc \ - --disable-boehm \ - --without-mcs-docs" -PKG_CONFIGURE_OPTS_HOST="$configure_opts --disable-libraries --enable-static" -PKG_CONFIGURE_OPTS_TARGET="$configure_opts --disable-mcs-build" +options="--build=$HOST_NAME \ + --prefix=$prefix \ + --bindir=$prefix/bin \ + --sbindir=$prefix/sbin \ + --sysconfdir=$prefix/etc \ + --libexecdir=$prefix/lib \ + --localstatedir=/var \ + --disable-boehm \ + --disable-libraries \ + --without-mcs-docs" -pre_configure_host() { +configure_host() { cp -PR ../* . + ./configure $options --host=$HOST_NAME } makeinstall_host() { : # nop } -pre_configure_target() { +configure_target() { cp -PR ../* . + strip_lto + ./configure $options --host=$TARGET_NAME \ + --disable-mcs-build } makeinstall_target() { make -C "$ROOT/$PKG_BUILD/.$HOST_NAME" install DESTDIR="$INSTALL" make -C "$ROOT/$PKG_BUILD/.$TARGET_NAME" install DESTDIR="$INSTALL" - rm -fr "$INSTALL/storage/.kodi/addons/$PKG_SECTION.$PKG_NAME/include" - rm -fr "$INSTALL/storage/.kodi/addons/$PKG_SECTION.$PKG_NAME/share/man" $STRIP "$INSTALL/storage/.kodi/addons/$PKG_SECTION.$PKG_NAME/bin/mono" } addon() { - mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID" - cp -PR "$PKG_BUILD/.install_pkg/storage/.kodi/addons/$PKG_SECTION.$PKG_NAME"/* "$ADDON_BUILD/$PKG_ADDON_ID/" + mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID" + + cp -PR "$PKG_BUILD/.install_pkg/storage/.kodi/addons/$PKG_SECTION.$PKG_NAME"/* \ + "$ADDON_BUILD/$PKG_ADDON_ID/" - mkdir -p $ADDON_BUILD/$PKG_ADDON_ID/lib - for p in \ - bigreqsproto \ - cairo \ - inputproto \ - kbproto \ - libexif \ - libpthread-stubs \ - libX11 \ - libXau \ - libxcb \ - libXext \ - pixman \ - xcb-proto \ - xcmiscproto \ - xextproto \ - xproto \ - xtrans \ - libgdiplus \ - mono_sqlite - do - d=$(get_build_dir $p)/.install_pkg/usr/lib - if [ -d $d ] - then - cp -PR $d/* $ADDON_BUILD/$PKG_ADDON_ID/lib/ - fi - done + rm -fr "$ADDON_BUILD/$PKG_ADDON_ID/include" \ + "$ADDON_BUILD/$PKG_ADDON_ID/share/man" + + mv "$ADDON_BUILD/$PKG_ADDON_ID/bin/mono-sgen" \ + "$ADDON_BUILD/$PKG_ADDON_ID/bin/mono" + + cp -L "$(get_build_dir cairo)/.install_pkg/usr/lib/libcairo.so.2" \ + "$(get_build_dir libX11)/.install_pkg/usr/lib/libX11.so.6" \ + "$(get_build_dir libXext)/.install_pkg/usr/lib/libXext.so.6" \ + "$(get_build_dir libexif)/.install_pkg/usr/lib/libexif.so.12" \ + "$(get_build_dir libgdiplus)/.install_pkg/usr/lib/libgdiplus.so" \ + "$(get_build_dir mono_sqlite)/.install_pkg/usr/lib/libsqlite3.so.0" \ + "$(get_build_dir pixman)/.install_pkg/usr/lib/libpixman-1.so.0" \ + "$ADDON_BUILD/$PKG_ADDON_ID/lib" } diff --git a/packages/addons/tools/mono/patches/mono-01.debian-changes.patch b/packages/addons/tools/mono/patches/mono-01.debian-changes.patch new file mode 100644 index 0000000000..609938d752 --- /dev/null +++ b/packages/addons/tools/mono/patches/mono-01.debian-changes.patch @@ -0,0 +1,12675 @@ +Description: + 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-8) unstable; urgency=medium + . + * [cf4937c] Fixes for ARM64 packages. Thanks to Matthias Klose + * [ec6abc9] Further packaging fixes for ARM64 (Closes: 825497) +Author: Jo Shields +Bug-Debian: https://bugs.debian.org/825497 + +--- +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: , +Bug: +Bug-Debian: https://bugs.debian.org/ +Bug-Ubuntu: https://launchpad.net/bugs/ +Forwarded: +Reviewed-By: +Last-Update: + +--- 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 @@ + + + +- ++ + + + +--- 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 @@ + + + +- ++ + + + +--- 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 @@ + + + +- ++ + + + +--- 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 @@ + + + +- ++ + + + +--- 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 () ++ { ++ } ++ ++ ++ /// ++ /// Navigate to the page in the history, by index. ++ /// ++ /// ++ /// A representing an absolute index in the ++ /// history (that is, > -1 and < history length ++ /// ++ public void Go (int index) ++ { ++ } ++ ++ /// ++ /// ++ /// ++ /// ++ /// A representing an index in the ++ /// history, that can be relative or absolute depending on the relative argument ++ /// ++ /// ++ /// A 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. ++ /// ++ 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= 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 ++// ++// 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 ++// ++// 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 _dictionary; ++ readonly bool useOdbcRules; ++ ++ #endregion Fields ++ ++ #region Constructors ++ ++ public DbConnectionStringBuilder () : this (false) ++ { ++ } ++ ++ public DbConnectionStringBuilder (bool useOdbcRules) ++ { ++ this.useOdbcRules = useOdbcRules; ++ _dictionary = new Dictionary (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 dictionary = (IDictionary ) _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) _dictionary.Keys).CopyTo (keys, 0); ++ ReadOnlyCollection keyColl = new ReadOnlyCollection (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) _dictionary.Values).CopyTo (values, 0); ++ ReadOnlyCollection valuesColl = new ReadOnlyCollection (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 [] arr = array as KeyValuePair []; ++ if (arr == null) ++ throw new ArgumentException ("Target array type is not compatible with the type of items in the collection"); ++ ((ICollection>) _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 ++// ++// (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 ++// ++// (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 keys = new List (); ++ 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 ++// ++// 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 ++// ++// 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 ++// ++// 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 @@ + 2 + + .NETFramework +- v4.0 ++ v4.5 + + $(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion),Profile=$(TargetFrameworkProfile) + $(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion) +--- 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 @@ + 2 + + .NETFramework +- v4.0 ++ v4.5 + + $(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion),Profile=$(TargetFrameworkProfile) + $(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion) +--- 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 @@ + 2 + + .NETFramework +- v4.0 ++ v4.5 + + $(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion),Profile=$(TargetFrameworkProfile) + $(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion) +--- /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 ++ ++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 ++#include ++ ++#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 ++#include ++#include ++#include ++ ++/* ++ * 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 -> ++ * ++ * fp -> ++ * sp -> ++ */ ++ 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 ++ ++#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 ++#include ++ ++#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, ] */ ++ 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 + +-#if defined(__linux__) && !defined(PLATFORM_ANDROID) ++#if (defined(__linux__) && !defined(PLATFORM_ANDROID)) || defined(__FreeBSD_kernel__) + + #include + #include +--- 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; + } + +